From e924920bec73b18ffbf44bd01856b25afab35393 Mon Sep 17 00:00:00 2001 From: Joffrey JAFFEUX Date: Mon, 11 Sep 2017 19:14:22 +0200 Subject: [PATCH] uses select-box for pinned-button This commit also moves more logic in dropdown-select-box instead of duplicating it for notifications-options and pinned-options --- .../categories-admin-dropdown.js.es6 | 17 ----- .../category-notifications-button.js.es6 | 1 - .../components/dropdown-select-box.js.es6 | 25 ++++++- .../discourse/components/pinned-button.js.es6 | 75 ++++--------------- .../components/pinned-options.js.es6 | 70 +++++++++++++++++ .../discourse/components/select-box.js.es6 | 8 +- .../select-box/select-box-row.js.es6 | 11 +++ .../dropdown-select-box/dropdown-header.hbs | 4 +- .../templates/components/pinned-button.hbs | 5 ++ .../common/base/notifications-button.scss | 14 ++-- .../components/categories-admin-dropdown.scss | 10 --- .../category-notifications-button.scss | 3 - .../components/dropdown-select-box.scss | 7 +- .../common/components/pinned-button.scss | 44 +++++++++++ .../common/components/select-box.scss | 16 ++++ .../components/pinned-button-test.js.es6 | 44 +++++++++++ .../helpers/create-pretender.js.es6 | 3 +- 17 files changed, 249 insertions(+), 108 deletions(-) create mode 100644 app/assets/javascripts/discourse/components/pinned-options.js.es6 create mode 100644 app/assets/javascripts/discourse/templates/components/pinned-button.hbs delete mode 100644 app/assets/stylesheets/common/components/category-notifications-button.scss create mode 100644 app/assets/stylesheets/common/components/pinned-button.scss create mode 100644 test/javascripts/components/pinned-button-test.js.es6 diff --git a/app/assets/javascripts/discourse/components/categories-admin-dropdown.js.es6 b/app/assets/javascripts/discourse/components/categories-admin-dropdown.js.es6 index 2723523c209..caabc304f07 100644 --- a/app/assets/javascripts/discourse/components/categories-admin-dropdown.js.es6 +++ b/app/assets/javascripts/discourse/components/categories-admin-dropdown.js.es6 @@ -10,8 +10,6 @@ export default DropdownSelectBoxComponent.extend({ generatedHeadertext: null, - fullWidthOnMobile: true, - @computed content() { const items = [ @@ -45,20 +43,5 @@ export default DropdownSelectBoxComponent.extend({ _didSelectRow() { this.sendAction(`actionNames.${this.get("value")}`); this.set("value", null); - }, - - @computed - templateForRow: function() { - return (rowComponent) => { - const content = rowComponent.get("content"); - - return ` -
${iconHTML(content.icon)}
-
- ${Handlebars.escapeExpression(content.text)} - ${Handlebars.escapeExpression(content.description)} -
- `; - }; } }); diff --git a/app/assets/javascripts/discourse/components/category-notifications-button.js.es6 b/app/assets/javascripts/discourse/components/category-notifications-button.js.es6 index 89fcb5c7a56..c5f0b51eae4 100644 --- a/app/assets/javascripts/discourse/components/category-notifications-button.js.es6 +++ b/app/assets/javascripts/discourse/components/category-notifications-button.js.es6 @@ -6,7 +6,6 @@ import { iconHTML } from "discourse-common/lib/icon-library"; export default NotificationOptionsComponent.extend({ classNames: ["category-notifications-button"], - classNameBindings: ["hidden:is-hidden"], hidden: Ember.computed.or("category.deleted", "site.isMobileDevice"), i18nPrefix: "category.notifications", diff --git a/app/assets/javascripts/discourse/components/dropdown-select-box.js.es6 b/app/assets/javascripts/discourse/components/dropdown-select-box.js.es6 index 1135bddd019..b268da2aa81 100644 --- a/app/assets/javascripts/discourse/components/dropdown-select-box.js.es6 +++ b/app/assets/javascripts/discourse/components/dropdown-select-box.js.es6 @@ -1,3 +1,4 @@ +import computed from "ember-addons/ember-computed-decorators"; import SelectBoxComponent from "discourse/components/select-box"; export default SelectBoxComponent.extend({ @@ -5,6 +6,28 @@ export default SelectBoxComponent.extend({ wrapper: false, verticalOffset: 3, collectionHeight: "auto", + fullWidthOnMobile: true, + selectBoxHeaderComponent: "dropdown-select-box/dropdown-header", - selectBoxHeaderComponent: "dropdown-select-box/dropdown-header" + @computed + templateForRow: function() { + return (rowComponent) => { + let template = ""; + const content = rowComponent.get("content"); + + const icon = rowComponent.icon(); + if (icon) { + template += `
${icon}
`; + } + + template += ` +
+ ${Handlebars.escapeExpression(Ember.get(content, this.get("textKey")))} + ${Handlebars.escapeExpression(content.description)} +
+ `; + + return template; + }; + } }); diff --git a/app/assets/javascripts/discourse/components/pinned-button.js.es6 b/app/assets/javascripts/discourse/components/pinned-button.js.es6 index fe6dac72ded..3d287deb2ad 100644 --- a/app/assets/javascripts/discourse/components/pinned-button.js.es6 +++ b/app/assets/javascripts/discourse/components/pinned-button.js.es6 @@ -1,67 +1,22 @@ -import { iconHTML } from 'discourse-common/lib/icon-library'; -import computed from 'ember-addons/ember-computed-decorators'; -import DropdownButton from 'discourse/components/dropdown-button'; +import computed from "ember-addons/ember-computed-decorators"; -export default DropdownButton.extend({ - descriptionKey: 'help', - classNames: ['pinned-options'], - title: '', - buttonExtraClasses: 'btn-icon-text', +export default Ember.Component.extend({ + descriptionKey: "help", - longDescription: function(){ - const topic = this.get('topic'); - const globally = topic.get('pinned_globally') ? '_globally' : ''; - const key = 'topic_statuses.' + (topic.get('pinned') ? 'pinned' + globally : 'unpinned') + '.help'; + classNames: ["pinned-button"], + + classNameBindings: ["hidden:is-hidden"], + + @computed("topic.pinned_globally", "topic.pinned") + reasonText(pinnedGlobally, pinned) { + const globally = pinnedGlobally ? "_globally" : ""; + const pinnedKey = pinned ? `pinned${globally}` : "unpinned"; + const key = `topic_statuses.${pinnedKey}.help`; return I18n.t(key); - }.property('topic.pinned'), - - target: Em.computed.alias('topic'), - - hidden: function(){ - const topic = this.get('topic'); - return topic.get('deleted') || (!topic.get('pinned') && !topic.get('unpinned')); - }.property('topic.pinned', 'topic.deleted', 'topic.unpinned'), - - activeItem: function(){ - return this.get('topic.pinned') ? 'pinned' : 'unpinned'; - }.property('topic.pinned'), - - dropDownContent: function() { - const globally = this.get('topic.pinned_globally') ? '_globally' : ''; - return [ - {id: 'pinned', - title: I18n.t('topic_statuses.pinned' + globally + '.title'), - description: I18n.t('topic_statuses.pinned' + globally + '.help'), - icon: 'thumb-tack' }, - {id: 'unpinned', - title: I18n.t('topic_statuses.unpinned.title'), - description: I18n.t('topic_statuses.unpinned.help'), - icon: 'thumb-tack', - iconClass: 'unpinned' } - ]; - }.property(), - - @computed('topic.pinned', 'topic.pinned_globally') - text(pinned, pinnedGlobally) { - const globally = pinnedGlobally ? '_globally' : ''; - const state = pinned ? 'pinned' + globally : 'unpinned'; - - const icon = iconHTML( - 'thumb-tack', - { tagName: 'span', class: (state === 'unpinned' ? 'unpinned' : null) } - ); - - return icon + - I18n.t('topic_statuses.' + state + '.title') + ""; }, - clicked(id) { - const topic = this.get('topic'); - if(id==='unpinned'){ - topic.clearPin(); - } else { - topic.rePin(); - } + @computed("topic.pinned", "topic.deleted", "topic.unpinned") + hidden(pinned, deleted, unpinned) { + return deleted || (!pinned && !unpinned); } - }); diff --git a/app/assets/javascripts/discourse/components/pinned-options.js.es6 b/app/assets/javascripts/discourse/components/pinned-options.js.es6 new file mode 100644 index 00000000000..b9c4dbe7b2b --- /dev/null +++ b/app/assets/javascripts/discourse/components/pinned-options.js.es6 @@ -0,0 +1,70 @@ +import DropdownSelectBoxComponent from "discourse/components/dropdown-select-box"; +import computed from "ember-addons/ember-computed-decorators"; +import { observes } from "ember-addons/ember-computed-decorators"; +import { iconHTML } from "discourse-common/lib/icon-library"; + +export default DropdownSelectBoxComponent.extend({ + classNames: ["pinned-options"], + + @computed("topic.pinned") + value(pinned) { + return pinned ? "pinned" : "unpinned"; + }, + + @observes("topic.pinned") + _pinnedChanged() { + this.set("value", this.get("topic.pinned") ? "pinned" : "unpinned"); + }, + + @computed("topic.pinned_globally") + content(pinnedGlobally) { + const globally = pinnedGlobally ? "_globally" : ""; + + return [ + { + id: "pinned", + text: I18n.t("topic_statuses.pinned" + globally + ".title"), + description: I18n.t('topic_statuses.pinned' + globally + '.help'), + icon: "thumb-tack" + }, + { + id: "unpinned", + text: I18n.t("topic_statuses.unpinned.title"), + icon: "thumb-tack", + description: I18n.t('topic_statuses.unpinned.help'), + iconClass: "unpinned" + } + ]; + }, + + @computed("topic.pinned", "topic.pinned_globally") + icon(pinned, pinnedGlobally) { + const globally = pinnedGlobally ? "_globally" : ""; + const state = pinned ? `pinned${globally}` : "unpinned"; + + return iconHTML( + "thumb-tack", + { class: (state === "unpinned" ? "unpinned" : null) } + ); + }, + + @computed("topic.pinned", "topic.pinned_globally") + generatedHeadertext(pinned, pinnedGlobally) { + const globally = pinnedGlobally ? "_globally" : ""; + const state = pinned ? `pinned${globally}` : "unpinned"; + const title = I18n.t(`topic_statuses.${state}.title`); + + return `${title}${iconHTML("caret-down")}`.htmlSafe(); + }, + + @observes("value") + _didSelectRow() { + const topic = this.get("topic"); + + if (this.get("value") === "unpinned") { + topic.clearPin(); + } else { + topic.rePin(); + } + } +}); diff --git a/app/assets/javascripts/discourse/components/select-box.js.es6 b/app/assets/javascripts/discourse/components/select-box.js.es6 index 8d6f308a8c3..89aa8ef14f9 100644 --- a/app/assets/javascripts/discourse/components/select-box.js.es6 +++ b/app/assets/javascripts/discourse/components/select-box.js.es6 @@ -1,17 +1,17 @@ import { on, observes } from "ember-addons/ember-computed-decorators"; import computed from "ember-addons/ember-computed-decorators"; -import { iconHTML } from "discourse-common/lib/icon-library"; export default Ember.Component.extend({ layoutName: "components/select-box", classNames: "select-box", - classNameBindings: ["expanded:is-expanded"], + classNameBindings: ["expanded:is-expanded", "hidden:is-hidden"], expanded: false, focused: false, filterFocused: false, renderBody: false, wrapper: true, + hidden: false, tabindex: 0, scrollableParentSelector: ".modal-body", @@ -86,9 +86,7 @@ export default Ember.Component.extend({ return (rowComponent) => { let template = ""; - if (rowComponent.get("content.icon")) { - template += iconHTML(Handlebars.escapeExpression(rowComponent.get("content.icon"))); - } + template += rowComponent.icon(); const text = rowComponent.get(`content.${this.get("textKey")}`); template += `

${Handlebars.escapeExpression(text)}

`; diff --git a/app/assets/javascripts/discourse/components/select-box/select-box-row.js.es6 b/app/assets/javascripts/discourse/components/select-box/select-box-row.js.es6 index e4ebb84e53a..f0e359dafb4 100644 --- a/app/assets/javascripts/discourse/components/select-box/select-box-row.js.es6 +++ b/app/assets/javascripts/discourse/components/select-box/select-box-row.js.es6 @@ -1,4 +1,5 @@ import computed from 'ember-addons/ember-computed-decorators'; +import { iconHTML } from "discourse-common/lib/icon-library"; export default Ember.Component.extend({ layoutName: "components/select-box/select-box-row", @@ -31,6 +32,16 @@ export default Ember.Component.extend({ return shouldSelectRow(this); }, + icon() { + if (this.get("content.icon")) { + const iconName = this.get("content.icon"); + const iconClass = this.get("content.iconClass"); + return iconHTML(iconName, { class: iconClass }); + } + + return null; + }, + mouseEnter() { this.sendAction("onHover", this.get("content")); }, diff --git a/app/assets/javascripts/discourse/templates/components/dropdown-select-box/dropdown-header.hbs b/app/assets/javascripts/discourse/templates/components/dropdown-select-box/dropdown-header.hbs index 034ae8912b7..d136e357e1c 100644 --- a/app/assets/javascripts/discourse/templates/components/dropdown-select-box/dropdown-header.hbs +++ b/app/assets/javascripts/discourse/templates/components/dropdown-select-box/dropdown-header.hbs @@ -4,9 +4,9 @@ type="button" title="{{text}}"> - {{icon}} + {{{icon}}} {{#if text}} - {{text}} + {{{text}}} {{/if}} diff --git a/app/assets/javascripts/discourse/templates/components/pinned-button.hbs b/app/assets/javascripts/discourse/templates/components/pinned-button.hbs new file mode 100644 index 00000000000..aaabc2f1bf3 --- /dev/null +++ b/app/assets/javascripts/discourse/templates/components/pinned-button.hbs @@ -0,0 +1,5 @@ +{{pinned-options topic=topic}} + +

+ {{{reasonText}}} +

diff --git a/app/assets/stylesheets/common/base/notifications-button.scss b/app/assets/stylesheets/common/base/notifications-button.scss index b7bcf8513e8..63d3c2cf135 100644 --- a/app/assets/stylesheets/common/base/notifications-button.scss +++ b/app/assets/stylesheets/common/base/notifications-button.scss @@ -1,7 +1,9 @@ -.d-icon.regular, .d-icon.muted, .d-icon.watching-first-post { - color: dark-light-choose(scale-color($primary, $lightness: 40%), scale-color($secondary, $lightness: 60%)); -} -.d-icon.tracking, .d-icon.watching { - color: $tertiary; - font-weight: normal; +.notifications-button.notifications-button.notifications-button { + .d-icon.regular, .d-icon.muted, .d-icon.watching-first-post { + color: dark-light-choose(scale-color($primary, $lightness: 40%), scale-color($secondary, $lightness: 60%)); + } + .d-icon.tracking, .d-icon.watching { + color: $tertiary; + font-weight: normal; + } } diff --git a/app/assets/stylesheets/common/components/categories-admin-dropdown.scss b/app/assets/stylesheets/common/components/categories-admin-dropdown.scss index 45c494fcdcd..de32ec0a6ff 100644 --- a/app/assets/stylesheets/common/components/categories-admin-dropdown.scss +++ b/app/assets/stylesheets/common/components/categories-admin-dropdown.scss @@ -3,14 +3,4 @@ min-width: auto; width: 250px; } - - .select-box-row .icons { - align-self: flex-start; - width: 30px; - justify-content: center; - - .d-icon { - color: dark-light-choose(scale-color($primary, $lightness: 40%), scale-color($secondary, $lightness: 60%)); - } - } } diff --git a/app/assets/stylesheets/common/components/category-notifications-button.scss b/app/assets/stylesheets/common/components/category-notifications-button.scss deleted file mode 100644 index 61a0bd3ef60..00000000000 --- a/app/assets/stylesheets/common/components/category-notifications-button.scss +++ /dev/null @@ -1,3 +0,0 @@ -.category-notification-button.category-notification-button.is-hidden { - display: none; -} diff --git a/app/assets/stylesheets/common/components/dropdown-select-box.scss b/app/assets/stylesheets/common/components/dropdown-select-box.scss index dc91d643671..385a0936bc6 100644 --- a/app/assets/stylesheets/common/components/dropdown-select-box.scss +++ b/app/assets/stylesheets/common/components/dropdown-select-box.scss @@ -16,6 +16,7 @@ background-clip: padding-box; box-shadow: 0 1px 5px rgba(0,0,0,0.4); max-width: 300px; + width: 300px; } .select-box-row { @@ -25,16 +26,18 @@ .icons { display: flex; align-items: flex-start; - justify-content: space-between; - align-self: center; + justify-content: center; + align-self: flex-start; margin-right: 10px; margin-top: 2px; + width: 30px; .d-icon { font-size: 1.286em; align-self: center; margin-right: 0; opacity: 1; + color: dark-light-choose(scale-color($primary, $lightness: 40%), scale-color($secondary, $lightness: 60%)); } } diff --git a/app/assets/stylesheets/common/components/pinned-button.scss b/app/assets/stylesheets/common/components/pinned-button.scss new file mode 100644 index 00000000000..78ac146c3e5 --- /dev/null +++ b/app/assets/stylesheets/common/components/pinned-button.scss @@ -0,0 +1,44 @@ +#topic-footer-buttons { + .pinned-button { + min-width: auto; + margin: 10px 0 5px 0; + + &.is-hidden { + display: none; + } + + .btn { + margin: 0; + } + + .reason { + line-height: 16px; + margin: 0 0 0 10px; + + a { + margin-left: 3px; + margin-right: 3px; + } + } + } +} + +.pinned-button { + display: flex; + justify-content: flex-start; + align-items: center; + margin: 0; + min-width: auto; + + .pinned-options, .reason { + display: inline-flex; + } + + .pinned-options.pinned-options.pinned-options { + .select-box-body { + min-width: unset; + max-width: unset; + width: 550px; + } + } +} diff --git a/app/assets/stylesheets/common/components/select-box.scss b/app/assets/stylesheets/common/components/select-box.scss index f8cfce949ed..c87f5effa85 100644 --- a/app/assets/stylesheets/common/components/select-box.scss +++ b/app/assets/stylesheets/common/components/select-box.scss @@ -11,6 +11,10 @@ height: 34px; min-width: 220px; + &.is-hidden { + display: none; + } + &.small { height: 28px; } @@ -128,6 +132,18 @@ margin-left: 5px; pointer-events: none; } + + .d-button-label { + display: flex; + align-items: center; + white-space: nowrap; + overflow: hidden; + + .d-icon { + margin-left: 5px; + margin-right: 0; + } + } } .select-box-body { diff --git a/test/javascripts/components/pinned-button-test.js.es6 b/test/javascripts/components/pinned-button-test.js.es6 new file mode 100644 index 00000000000..f58681fd511 --- /dev/null +++ b/test/javascripts/components/pinned-button-test.js.es6 @@ -0,0 +1,44 @@ +import componentTest from 'helpers/component-test'; +import Topic from 'discourse/models/topic'; + +const buildTopic = function() { + return Topic.create({ + id: 1234, + title: "Qunit Test Topic", + deleted: false, + pinned: true + }); +}; + +moduleForComponent('pinned-button', { integration: true }); + +componentTest('updating the content refreshes the list', { + template: '{{pinned-button topic=topic}}', + + beforeEach() { + this.siteSettings.automatically_unpin_topics = false; + this.set("topic", buildTopic()); + }, + + test(assert) { + andThen(() => { + assert.equal(find(".pinned-button").hasClass("is-hidden"), false); + }); + + click(".select-box-header"); + + andThen(() => { + assert.equal(find(".select-box-row.is-selected .title").html().trim(), "Pinned"); + }); + + andThen(() => { + this.set("topic.pinned", false); + assert.equal(find(".select-box-row.is-selected .title").html().trim(), "Unpinned"); + }); + + andThen(() => { + this.set("topic.deleted", true); + assert.equal(find(".pinned-button").hasClass("is-hidden"), true); + }); + } +}); diff --git a/test/javascripts/helpers/create-pretender.js.es6 b/test/javascripts/helpers/create-pretender.js.es6 index 9324d9840cf..e780c9f04d8 100644 --- a/test/javascripts/helpers/create-pretender.js.es6 +++ b/test/javascripts/helpers/create-pretender.js.es6 @@ -129,6 +129,7 @@ export default function() { this.get("/t/28830.json", () => response(fixturesByUrl['/t/28830/1.json'])); this.get("/t/9.json", () => response(fixturesByUrl['/t/9/1.json'])); this.get("/t/12.json", () => response(fixturesByUrl['/t/12/1.json'])); + this.put("/t/1234/re-pin", success); this.get("/t/id_for/:slug", () => { return response({id: 280, slug: "internationalization-localization", url: "/t/internationalization-localization/280"}); @@ -400,4 +401,4 @@ export default function() { server.checkPassthrough = request => request.requestHeaders['Discourse-Script']; return server; -} \ No newline at end of file +}