Add d-link component to simplify menu markup

This commit is contained in:
Robin Ward 2015-09-01 16:14:34 -04:00
parent 6fb69d4434
commit c36fdccab2
4 changed files with 152 additions and 24 deletions

View File

@ -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));
}
}
});

View File

@ -5,7 +5,16 @@ export default Ember.Component.extend({
classNames: ['user-menu'], classNames: ['user-menu'],
notifications: null, notifications: null,
loadingNotifications: false, 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') @observes('visible')
_loadNotifications(visible) { _loadNotifications(visible) {

View File

@ -1,45 +1,41 @@
{{#menu-panel visible=visible}} {{#menu-panel visible=visible}}
<div class='menu-links-header'> <div class='menu-links-header'>
<ul class='menu-links-row'> <ul class='menu-links-row'>
<li>{{#link-to 'user' currentUser class="user-activity-link" }}<i class='fa fa-user'></i> {{i18n 'user.profile'}}{{/link-to}}</li> <li>{{d-link route='user' model=currentUser class="user-activity-link" icon="user" label="user.profile"}}</li>
{{#if allowAnon}} {{#if showDisableAnon}}
{{#if isAnon}} <li>{{d-link action="toggleAnon" label="switch_from_anon"}}</li>
<li>
<a href {{action 'toggleAnon'}}>{{i18n 'switch_from_anon'}}</a>
</li>
{{/if}}
{{/if}}
<li class='glyphs'>
<a href="{{currentUser.path}}/activity/bookmarks" title="{{i18n 'user.bookmarks'}}" aria-label="{{i18n 'user.bookmarks'}}"><i class='fa fa-bookmark'></i></a>
<a href="{{currentUser.path}}/messages" title="{{i18n 'user.private_messages'}}" aria-label="{{i18n 'user.private_messages'}}"><i class='fa fa-envelope'></i></a>
{{#if allowAnon}}
{{#unless isAnon}}
<a href {{action 'toggleAnon'}} title="{{i18n 'switch_to_anon'}}" aria-label="{{i18n 'switch_to_anon'}}"><i class='fa fa-user-secret'></i></a>
{{/unless}}
{{/if}} {{/if}}
<a href="{{currentUser.path}}/preferences" title="{{i18n 'user.preferences'}}" aria-label="{{i18n 'user.preferences'}}"><i class='fa fa-gear'></i></a> <li class='glyphs'>
</li> {{d-link path=bookmarksPath title="user.bookmarks" icon="bookmark"}}
</ul> {{d-link path=messagesPath title="user.private_messages" icon="envelope"}}
{{#if showEnableAnon}}
{{d-link action="toggleAnon" title="switch_to_anon" icon="user-secret"}}
{{/if}}
{{d-link path=preferencesPath title="user.preferences" icon="gear"}}
</li>
</ul>
</div> </div>
<div class='notifications'> <div class='notifications'>
{{#conditional-loading-spinner condition=loadingNotifications containerClass="spinner-container"}} {{#conditional-loading-spinner condition=loadingNotifications containerClass="spinner-container"}}
<hr style="margin:3px 0" /> <hr>
{{#if notifications}} {{#if notifications}}
<ul> <ul>
{{#each notifications as |n|}} {{#each notifications as |n|}}
{{notification-item notification=n}} {{notification-item notification=n}}
{{/each}} {{/each}}
<li class="read last"> <li class="read last">
<a href={{myNotificationsUrl}}>{{i18n 'notifications.more'}}&hellip;</a> {{#d-link path=notificationsPath}}
{{i18n 'notifications.more'}}&hellip;
{{/d-link}}
</li> </li>
</ul> </ul>
{{/if}} {{/if}}
{{/conditional-loading-spinner}} {{/conditional-loading-spinner}}
{{#if siteSettings.show_logout_in_header}} {{#if siteSettings.show_logout_in_header}}
<hr style="margin:3px 0" /> <hr>
<a href {{action 'logout'}} class='logout'>{{fa-icon "sign-out"}} {{i18n 'user.log_out'}}</a> {{d-link action="logout" class="logout" icon="sign-out" label="user.log_out"}}
{{/if}} {{/if}}
{{plugin-outlet "user-menu-bottom"}} {{plugin-outlet "user-menu-bottom"}}
</div> </div>

View File

@ -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')
}
});