From c36fdccab29783cecebe504800d5452b40317af9 Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Tue, 1 Sep 2015 16:14:34 -0400 Subject: [PATCH] Add `d-link` component to simplify menu markup --- .../discourse/components/d-link.js.es6 | 62 +++++++++++++++++++ .../discourse/components/user-menu.js.es6 | 11 +++- .../templates/components/user-menu.hbs | 42 ++++++------- .../javascripts/components/d-link-test.js.es6 | 61 ++++++++++++++++++ 4 files changed, 152 insertions(+), 24 deletions(-) create mode 100644 app/assets/javascripts/discourse/components/d-link.js.es6 create mode 100644 test/javascripts/components/d-link-test.js.es6 diff --git a/app/assets/javascripts/discourse/components/d-link.js.es6 b/app/assets/javascripts/discourse/components/d-link.js.es6 new file mode 100644 index 00000000000..a0babf3040d --- /dev/null +++ b/app/assets/javascripts/discourse/components/d-link.js.es6 @@ -0,0 +1,62 @@ +import computed from 'ember-addons/ember-computed-decorators'; +import { iconHTML } from 'discourse/helpers/fa-icon'; +import DiscourseURL from 'discourse/lib/url'; + +export default Ember.Component.extend({ + tagName: 'a', + attributeBindings: ['translatedTitle:title', 'translatedTitle:aria-title', 'href'], + + @computed('path') + href(path) { + if (path) { return path; } + + const route = this.get('route'); + if (route) { + const router = this.container.lookup('router:main'); + if (router && router.router) { + return router.router.generate(route, this.get('model')); + } + } + + return ''; + }, + + @computed("title", "label") + translatedTitle(title, label) { + const text = title || label; + if (text) return I18n.t(text); + }, + + click() { + const action = this.get('action'); + if (action) { + this.sendAction('action'); + return false; + } + const href = this.get('href'); + if (href) { + DiscourseURL.routeTo(href); + return false; + } + return false; + }, + + render(buffer) { + if (!!this.get('template')) { + return this._super(buffer); + } + + const icon = this.get('icon'); + if (icon) { + buffer.push(iconHTML(icon)); + } + + const label = this.get('label'); + if (label) { + if (icon) { buffer.push(" "); } + + buffer.push(I18n.t(label)); + } + } + +}); diff --git a/app/assets/javascripts/discourse/components/user-menu.js.es6 b/app/assets/javascripts/discourse/components/user-menu.js.es6 index 1965640d976..a6a7e46be88 100644 --- a/app/assets/javascripts/discourse/components/user-menu.js.es6 +++ b/app/assets/javascripts/discourse/components/user-menu.js.es6 @@ -5,7 +5,16 @@ export default Ember.Component.extend({ classNames: ['user-menu'], notifications: null, loadingNotifications: false, - myNotificationsUrl: url('/my/notifications'), + notificationsPath: url('currentUser.path', '%@/notifications'), + bookmarksPath: url('currentUser.path', '%@/activity/bookmarks'), + messagesPath: url('currentUser.path', '%@/messages'), + preferencesPath: url('currentUser.path', '%@/preferences'), + + @computed('allowAnon', 'isAnon') + showEnableAnon(allowAnon, isAnon) { return allowAnon && !isAnon; }, + + @computed('allowAnon', 'isAnon') + showDisableAnon(allowAnon, isAnon) { return allowAnon && isAnon; }, @observes('visible') _loadNotifications(visible) { diff --git a/app/assets/javascripts/discourse/templates/components/user-menu.hbs b/app/assets/javascripts/discourse/templates/components/user-menu.hbs index 7a33b5999c1..acdd90aaa3a 100644 --- a/app/assets/javascripts/discourse/templates/components/user-menu.hbs +++ b/app/assets/javascripts/discourse/templates/components/user-menu.hbs @@ -1,45 +1,41 @@ {{#menu-panel visible=visible}}
{{#conditional-loading-spinner condition=loadingNotifications containerClass="spinner-container"}} -
+
{{#if notifications}} {{/if}} {{/conditional-loading-spinner}} {{#if siteSettings.show_logout_in_header}} -
- {{fa-icon "sign-out"}} {{i18n 'user.log_out'}} +
+ {{d-link action="logout" class="logout" icon="sign-out" label="user.log_out"}} {{/if}} {{plugin-outlet "user-menu-bottom"}}
diff --git a/test/javascripts/components/d-link-test.js.es6 b/test/javascripts/components/d-link-test.js.es6 new file mode 100644 index 00000000000..d0b96926950 --- /dev/null +++ b/test/javascripts/components/d-link-test.js.es6 @@ -0,0 +1,61 @@ +import componentTest from 'helpers/component-test'; + +moduleForComponent('d-link', {integration: true}); + +componentTest('basic usage', { + template: '{{d-link path="/wat" title="user.preferences" icon="gear"}}', + test(assert) { + const $a = this.$('a'); + + assert.ok($a.length); + assert.equal($a.attr('href'), '/wat'); + assert.equal($a.attr('title'), I18n.t('user.preferences')); + assert.equal($a.attr('aria-title'), I18n.t('user.preferences')); + assert.ok(this.$('i.fa-gear', $a).length, 'shows the icon'); + } +}); + +componentTest('with a label', { + template: '{{d-link label="user.preferences"}}', + test(assert) { + const $a = this.$('a'); + assert.equal($a.text(), I18n.t('user.preferences')); + assert.equal($a.attr('title'), I18n.t('user.preferences')); + assert.equal($a.attr('aria-title'), I18n.t('user.preferences')); + } +}); + +componentTest('with a label and icon', { + template: '{{d-link label="user.preferences" icon="gear"}}', + test(assert) { + const $a = this.$('a'); + assert.ok(this.$('i.fa-gear', $a).length, 'shows the icon'); + assert.equal($a.text(), ` ${I18n.t('user.preferences')}`, "includes a space"); + assert.equal($a.attr('title'), I18n.t('user.preferences')); + assert.equal($a.attr('aria-title'), I18n.t('user.preferences')); + } +}); + +componentTest('block form', { + template: '{{#d-link path="/test"}}hello world{{/d-link}}', + test(assert) { + const $a = this.$('a'); + assert.equal($a.attr('href'), '/test'); + assert.equal($a.text(), 'hello world'); + } +}); + +componentTest('with an action', { + template: '{{d-link action="doThing" title="user.preferences" icon="gear"}}', + test(assert) { + expect(2); + + assert.ok(this.$('a[href]').length, 'href attribute is present to help with styling'); + + this.on('doThing', () => { + assert.ok(true, 'it fired the action'); + }); + + click('a') + } +});