UX: Merge notifications and user dropdown

This commit is contained in:
Robin Ward 2015-08-28 14:32:53 -04:00
parent d4b987ff32
commit 3ae5a0a2df
26 changed files with 211 additions and 527 deletions

View File

@ -1,8 +1,8 @@
export default Ember.Component.extend({
classNameBindings: ['containerClass'],
classNameBindings: ['containerClass', 'condition:visible'],
containerClass: function() {
return (this.get('size') === 'small') ? 'inline-spinner' : undefined;
return this.get('size') === 'small' ? 'inline-spinner' : undefined;
}.property('size'),
render: function(buffer) {

View File

@ -0,0 +1,17 @@
export default Ember.Component.extend({
tagName: 'li',
classNameBindings: [':header-dropdown-toggle', 'active'],
active: Ember.computed.alias('toggleVisible'),
actions: {
toggle() {
if (this.siteSettings.login_required && !this.currentUser) {
this.sendAction('loginAction');
} else {
this.toggleProperty('toggleVisible');
}
this.appEvents.trigger('dropdowns:closeAll');
}
}
});

View File

@ -55,21 +55,14 @@ export default Ember.Component.extend({
@observes('viewMode', 'visible')
_visibleChanged() {
const isDropdown = (this.get('viewMode') === 'drop-down');
const markActive = this.get('markActive');
if (this.get('visible')) {
this.appEvents.on('dropdowns:closeAll', this, this.hide);
// Allow us to hook into things being shown
Ember.run.scheduleOnce('afterRender', () => this.sendAction('onVisible'));
if (isDropdown && markActive) {
$(markActive).addClass('active');
}
$('html').on('click.close-menu-panel', (e) => {
const $target = $(e.target);
if ($target.closest(markActive).length > 0) { return; }
if ($target.closest('.header-dropdown-toggle').length > 0) { return; }
if ($target.closest('.menu-panel').length > 0) { return; }
this.hide();
});
@ -77,9 +70,6 @@ export default Ember.Component.extend({
this._watchSizeChanges();
} else {
Ember.run.scheduleOnce('afterRender', () => this.sendAction('onHidden'));
if (markActive) {
$(markActive).removeClass('active');
}
$('html').off('click.close-menu-panel');
this._stopWatchingSize();
}

View File

@ -167,7 +167,7 @@ export default Ember.Component.extend({
keyDown(e) {
const term = this.get('searchService.term');
if (e.which === 13 && term && term.length >= this.siteSettings.min_search_term_length) {
this.set('searchVisible', false);
this.set('visible', false);
this.send('fullSearch');
}
}

View File

@ -0,0 +1,66 @@
import { url } from 'discourse/lib/computed';
import { default as computed, observes } from 'ember-addons/ember-computed-decorators';
export default Ember.Component.extend({
classNames: ['user-menu'],
notifications: null,
loadingNotifications: false,
myNotificationsUrl: url('/my/notifications'),
@observes('visible')
_loadNotifications(visible) {
if (visible) {
this.refreshNotifications();
}
},
@observes('currentUser.lastNotificationChange')
_resetCachedNotifications() {
const visible = this.get('visible');
if (!Discourse.get("hasFocus")) {
this.set('visible', false);
this.set('notifications', null);
return;
}
if (visible) {
this.refreshNotifications();
} else {
this.set('notifications', null);
}
},
refreshNotifications() {
if (this.get('loadingNotifications')) { return; }
this.set("loadingNotifications", true);
// TODO: It's a bit odd to use the store in a component, but this one really
// wants to reach out and grab notifications
const store = this.container.lookup('store:main');
store.find('notification', {recent: true}).then((notifications) => {
this.setProperties({ 'currentUser.unread_notifications': 0, notifications });
}).catch(() => {
this.set('notifications', null);
}).finally(() => {
this.set("loadingNotifications", false);
});
},
@computed()
allowAnon() {
return this.siteSettings.allow_anonymous_posting &&
(this.get("currentUser.trust_level") >= this.siteSettings.anonymous_posting_min_trust_level ||
this.get("isAnon"));
},
isAnon: Ember.computed.alias('currentUser.is_anonymous'),
actions: {
toggleAnon() {
Discourse.ajax("/users/toggle-anon", {method: 'POST'}).then(function(){
window.location.reload();
});
}
}
});

View File

@ -1,13 +1,11 @@
const HeaderController = Ember.Controller.extend({
topic: null,
showExtraInfo: null,
notifications: null,
loadingNotifications: false,
hamburgerVisible: false,
searchVisible: false,
userMenuVisible: false,
needs: ['application'],
loginRequired: Em.computed.alias('controllers.application.loginRequired'),
canSignUp: Em.computed.alias('controllers.application.canSignUp'),
showSignUpButton: function() {
@ -18,70 +16,13 @@ const HeaderController = Ember.Controller.extend({
return Discourse.User.current() && !this.get('topic.isPrivateMessage');
}.property('topic.isPrivateMessage'),
_resetCachedNotifications: function() {
// a bit hacky, but if we have no focus, hide notifications first
const visible = $("#notifications-dropdown").is(":visible");
if(!Discourse.get("hasFocus")) {
if(visible){
$("html").click();
}
this.set("notifications", null);
return;
}
if(visible){
this.refreshNotifications();
} else {
this.set("notifications", null);
}
}.observes("currentUser.lastNotificationChange"),
refreshNotifications: function(){
const self = this;
if (self.get("loadingNotifications")) { return; }
self.set("loadingNotifications", true);
this.store.find('notification', {recent: true}).then(function(notifications) {
self.setProperties({
'currentUser.unread_notifications': 0,
notifications
});
}).catch(function() {
self.setProperties({
notifications: null
});
}).finally(function() {
self.set("loadingNotifications", false);
});
},
actions: {
toggleStar() {
const topic = this.get('topic');
if (topic) topic.toggleStar();
return false;
},
showNotifications(headerView) {
const self = this;
if (self.get('currentUser.unread_notifications') || self.get('currentUser.unread_private_messages') || !self.get('notifications')) {
self.refreshNotifications();
}
headerView.showDropdownBySelector("#user-notifications");
},
toggleSearchMenu() {
this.appEvents.trigger('dropdowns:closeAll');
this.toggleProperty('searchVisible');
},
toggleHamburgerMenu() {
this.appEvents.trigger('dropdowns:closeAll');
this.toggleProperty('hamburgerVisible');
}
}
});

View File

@ -1,7 +0,0 @@
import { url } from 'discourse/lib/computed';
export default Ember.ArrayController.extend({
needs: ['header'],
loadingNotifications: Em.computed.alias('controllers.header.loadingNotifications'),
myNotificationsUrl: url('/my/notifications')
});

View File

@ -1,25 +0,0 @@
export default Ember.ArrayController.extend({
showAdminLinks: Em.computed.alias("currentUser.staff"),
allowAnon: function(){
return this.siteSettings.allow_anonymous_posting &&
(this.get("currentUser.trust_level") >= this.siteSettings.anonymous_posting_min_trust_level ||
this.get("isAnon"));
}.property(),
isAnon: function(){
return this.get("currentUser.is_anonymous");
}.property(),
actions: {
logout() {
Discourse.logout();
return false;
},
toggleAnon() {
Discourse.ajax("/users/toggle-anon", {method: 'POST'}).then(function(){
window.location.reload();
});
}
}
});

View File

@ -77,7 +77,6 @@ export function cleanDOM() {
$('.profiler-results .profiler-result').remove();
// Close some elements that may be open
$('.d-dropdown').hide();
$('header ul.icons li').removeClass('active');
$('[data-toggle="dropdown"]').parent().removeClass('open');
// close the lightbox

View File

@ -203,13 +203,6 @@ const TopicRoute = Discourse.Route.extend({
// In case we navigate from one topic directly to another
isTransitioning = false;
if (Discourse.Mobile.mobileView) {
// close the dropdowns on mobile
$('.d-dropdown').hide();
$('header ul.icons li').removeClass('active');
$('[data-toggle="dropdown"]').parent().removeClass('open');
}
controller.setProperties({
model,
editingTopic: false,

View File

@ -1,5 +1,5 @@
{{#menu-panel visible=hamburgerVisible markActive=".hamburger-dropdown"}}
<ul class="location-links">
{{#menu-panel visible=visible}}
<ul class="menu-links">
{{#if currentUser.staff}}
<li>
{{#link-to "admin" class="admin-link"}}

View File

@ -0,0 +1,9 @@
<a {{action "toggle"}} class='icon' href title={{i18n title}} aria-label={{i18n title}} id={{iconId}}>
{{#if showUser}}
{{bound-avatar currentUser "medium"}}
{{else}}
{{fa-icon icon}}
{{/if}}
</a>
{{yield}}

View File

@ -1,8 +1,4 @@
{{#menu-panel visible=searchVisible
markActive=".search-dropdown"
onVisible="showedSearch"
onHidden="cancelHighlight"}}
{{#menu-panel visible=visible onVisible="showedSearch" onHidden="cancelHighlight"}}
{{plugin-outlet "above-search"}}
{{search-text-field value=searchService.term id="search-term"}}

View File

@ -1,5 +1,5 @@
<section class='d-dropdown' id='user-dropdown'>
<ul class='user-dropdown-links'>
{{#menu-panel visible=visible}}
<ul class='menu-links'>
<li>{{#link-to 'user' currentUser class="user-activity-link" }}{{i18n 'user.profile'}}{{/link-to}}</li>
<li>
{{#link-to 'userPrivateMessages.index' currentUser class='user-messages-link'}}
@ -11,6 +11,21 @@
{{#if allowAnon}}
<li><a href {{action 'toggleAnon'}}>{{#if isAnon}}{{i18n 'switch_from_anon'}}{{else}}{{i18n 'switch_to_anon'}}{{/if}}</a></li>
{{/if}}
<li>{{d-button action="logout" class="btn-danger right logout" icon="sign-out" label="user.log_out"}}</li>
</ul>
</section>
<div class='notifications'>
{{#conditional-loading-spinner condition=loadingNotifications containerClass="spinner-container"}}
<h3>{{i18n "user.notifications"}}</h3>
{{#if notifications}}
<ul>
{{#each notifications as |n|}}
{{notification-item notification=n}}
{{/each}}
<li class="read last">
<a href={{myNotificationsUrl}}>{{i18n 'notifications.more'}}&hellip;</a>
</li>
</ul>
{{/if}}
{{/conditional-loading-spinner}}
</div>
{{/menu-panel}}

View File

@ -1,5 +1,6 @@
{{hamburger-menu hamburgerVisible=hamburgerVisible showKeyboardAction="showKeyboardShortcutsHelp"}}
{{search-menu searchVisible=searchVisible}}
{{user-menu visible=userMenuVisible}}
{{hamburger-menu visible=hamburgerVisible showKeyboardAction="showKeyboardShortcutsHelp"}}
{{search-menu visible=searchVisible}}
<div class='wrap'>
<div class='contents clearfix'>
@ -14,75 +15,42 @@
{{/unless}}
<ul class='icons clearfix' role='navigation'>
{{#if currentUser}}
{{plugin-outlet "header-before-notifications"}}
{{/if}}
<li class='notifications'>
<a class='icon' href {{action "showNotifications" target="view"}} data-notifications="notifications-dropdown" id='user-notifications' title='{{i18n 'notifications.title'}}'>
{{fa-icon "comment" label="notifications.title"}}
</a>
{{#header-dropdown iconId="search-button"
icon="search"
toggleVisible=searchVisible
loginAction="showLogin"
title="search.title"}}
{{/header-dropdown}}
{{#header-dropdown iconId="toggle-hamburger-menu"
icon="bars"
toggleVisible=hamburgerVisible
loginAction="showLogin"
title="hamburger_menu"}}
{{#if flaggedPostsCount}}
<a href='/admin/flags/active' title='{{i18n 'notifications.total_flagged'}}' class='badge-notification flagged-posts'>{{flaggedPostsCount}}</a>
{{/if}}
{{/header-dropdown}}
{{#if currentUser}}
{{#header-dropdown iconId="current-user"
showUser="true"
toggleVisible=userMenuVisible
loginAction="showLogin"
title="user.avatar.header_title"}}
{{#if currentUser.unread_notifications}}
<a href class='badge-notification unread-notifications'>{{currentUser.unread_notifications}}</a>
{{/if}}
{{#if currentUser.unread_private_messages}}
<a href class='badge-notification unread-private-messages'>{{currentUser.unread_private_messages}}</a>
{{/if}}
</li>
{{/header-dropdown}}
{{/if}}
<li class="search-dropdown">
{{#if loginRequired}}
<a id='search-button' class='icon expand' href aria-hidden="true" {{action "showLogin"}}>
{{fa-icon "search"}}
</a>
{{else}}
<a {{action "toggleSearchMenu"}} class='icon' href
id='search-button'
title={{i18n 'search.title'}}>
{{fa-icon "search" label="search.title"}}
</a>
{{/if}}
</li>
<li class='hamburger-dropdown'>
{{#if loginRequired}}
<a class='icon'
href
aria-hidden="true"
id="toggle-hamburger-menu"
{{action "showLogin"}}>
{{fa-icon "bars"}}
</a>
{{else}}
<a {{action "toggleHamburgerMenu"}} class='icon' href
title={{i18n 'hamburger_menu'}}
aria-label={{i18n 'hamburger_menu'}}
id="toggle-hamburger-menu">
{{fa-icon "bars"}}
</a>
{{/if}}
{{#if flaggedPostsCount}}
<a href='/admin/flags/active' title='{{i18n 'notifications.total_flagged'}}' class='badge-notification flagged-posts'>{{flaggedPostsCount}}</a>
{{/if}}
</li>
{{#if currentUser}}
<li class='current-user dropdown'>
<a class='icon'
data-dropdown="user-dropdown"
data-render="renderUserDropdown"
href="#"
title='{{i18n 'user.avatar.header_title'}}'
aria-label='{{i18n 'user.avatar.header_title'}}'
id="current-user">
{{bound-avatar currentUser "medium"}}
</a>
</li>
{{/if}}
</ul>
{{#if view.renderDropdowns}}
{{plugin-outlet "header-before-dropdowns"}}
{{render "notifications" notifications}}
{{render "user-dropdown"}}
{{/if}}
</ul>
</div>
{{#if showExtraInfo}}

View File

@ -1,16 +0,0 @@
<section class="d-dropdown" id="notifications-dropdown">
{{#conditional-loading-spinner condition=loadingNotifications}}
{{#if content}}
<ul>
{{#each n in model}}
{{notification-item notification=n}}
{{/each}}
<li class="read last">
<a href="{{unbound myNotificationsUrl}}">{{i18n 'notifications.more'}}&hellip;</a>
</li>
</ul>
{{else}}
<div class="none">{{i18n 'notifications.none'}}</div>
{{/if}}
{{/conditional-loading-spinner}}
</section>

View File

@ -1,105 +1,10 @@
let originalZIndex;
export default Ember.View.extend({
tagName: 'header',
classNames: ['d-header', 'clearfix'],
classNameBindings: ['editingTopic'],
templateName: 'header',
renderDropdowns: false,
showDropdown($target) {
var self = this;
this.appEvents.trigger('dropdowns:closeAll');
if (!this.get("renderDropdowns")) {
this.set("renderDropdowns", true);
Em.run.next(function(){
self.showDropdown($target);
});
return;
}
const elementId = $target.data('dropdown') || $target.data('notifications'),
$dropdown = $("#" + elementId),
$li = $target.closest('li'),
$ul = $target.closest('ul'),
$html = $('html'),
$header = $('header'),
replyZIndex = parseInt($('#reply-control').css('z-index'), 10);
originalZIndex = originalZIndex || $('header').css('z-index');
if (replyZIndex > 0) {
$header.css("z-index", replyZIndex + 1);
}
const controller = self.get('controller');
if (controller && !controller.isDestroyed) {
controller.set('visibleDropdown', elementId);
}
// we need to ensure we are rendered,
// this optimises the speed of the initial render
const render = $target.data('render');
if (render){
if (!this.get(render)){
this.set(render, true);
Em.run.next(this, function(){
this.showDropdown.apply(self, [$target]);
});
return;
}
}
const hideDropdown = function() {
$header.css("z-index", originalZIndex);
$dropdown.fadeOut('fast');
$li.removeClass('active');
$html.data('hide-dropdown', null);
if (controller && !controller.isDestroyed){
controller.set('visibleDropdown', null);
}
$html.off('click.d-dropdown');
$dropdown.off('click.d-dropdown');
};
// if a dropdown is active and the user clicks on it, close it
if ($li.hasClass('active')) { return hideDropdown(); }
// otherwhise, mark it as active
$li.addClass('active');
// hide the other dropdowns
$('li', $ul).not($li).removeClass('active');
$('.d-dropdown').not($dropdown).fadeOut('fast');
// fade it fast
$dropdown.fadeIn('fast');
// autofocus any text input field
$dropdown.find('input[type=text]').focus().select();
$html.on('click.d-dropdown', function(e) {
return $(e.target).closest('.d-dropdown').length > 0 ? true : hideDropdown();
});
$dropdown.on('click.d-dropdown', function(e) {
if(e.shiftKey || e.metaKey || e.ctrlKey || e.which === 2) return true;
return $(e.target).closest('a').not('.search-link, .filter-type').length > 0 ? hideDropdown() : true;
});
$html.data('hide-dropdown', hideDropdown);
return false;
},
showDropdownBySelector: function(selector) {
this.showDropdown($(selector));
},
showNotifications: function() {
this.get("controller").send("showNotifications", this);
},
examineDockHeader: function() {
var headerView = this;
// Check the dock after the current run loop. While rendering,
@ -124,29 +29,18 @@ export default Ember.View.extend({
}
}
});
},
_tearDown: function() {
$(window).unbind('scroll.discourse-dock');
$(document).unbind('touchmove.discourse-dock');
this.$('a.unread-private-messages, a.unread-notifications, a[data-notifications]').off('click.notifications');
this.$('a[data-dropdown]').off('click.dropdown');
$('body').off('keydown.header');
}.on('willDestroyElement'),
_setup: function() {
const self = this;
this.$('a[data-dropdown]').on('click.dropdown', function(e) {
self.showDropdown.call(self, $(e.currentTarget));
return false;
});
this.$().on('click.notifications','a.unread-private-messages, a.unread-notifications, a[data-notifications]', function(e) {
self.showNotifications(e);
return false;
});
$(window).bind('scroll.discourse-dock', function() {
self.examineDockHeader();
});
@ -154,22 +48,5 @@ export default Ember.View.extend({
self.examineDockHeader();
});
self.examineDockHeader();
// Delegate ESC to the composer
$('body').on('keydown.header', function(e) {
// Hide dropdowns
if (e.which === 27) {
self.$('li').removeClass('active');
self.$('.d-dropdown').fadeOut('fast');
}
if (self.get('editingTopic')) {
if (e.which === 13) {
self.finishedEdit();
}
if (e.which === 27) {
return self.cancelEdit();
}
}
});
}.on('didInsertElement')
});

View File

@ -126,7 +126,7 @@
background-color: scale-color($tertiary, $lightness: 50%);
}
.unread-private-messages {
left: -4px;
left: 82px;
}
}
.flagged-posts {
@ -134,96 +134,6 @@
}
}
.d-dropdown {
display: none;
position: absolute;
background: $secondary;
max-height: 417px;
top: 100%;
right: 0;
z-index: 1100;
overflow: auto;
border: 1px solid dark-light-diff($primary, $secondary, 90%, -60%);
padding: 5px;
box-shadow: 0 2px 2px rgba(0,0,0, .4);
ul {
margin: 0;
list-style: none;
}
.heading a:hover {
background-color: dark-light-diff($highlight, $secondary, 50%, -55%);
}
.selected {
background-color: dark-light-diff($tertiary, $secondary, 85%, -65%);
}
// Notifications
&#notifications-dropdown {
.fa { color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%)); }
.icon { color: dark-light-choose(scale-color($primary, $lightness: 30%), scale-color($secondary, $lightness: 70%)); }
li {
background-color: dark-light-diff($tertiary, $secondary, 85%, -65%);
i {
float: left;
margin-right: 5px;
padding-top: 2px;
}
span { color: $primary; }
&:hover a { background-color: dark-light-diff($highlight, $secondary, 50%, -55%); }
a { padding: 4px 0 3px 2px; }
p {
margin: 0;
overflow: hidden;
}
}
.is-warning {
i.fa-envelope-o {
&:before {
content: "\f0e0";
}
color: $danger;
}
}
.read {
background-color: $secondary;
}
.none {
padding: 5px;
}
.spinner {
width: 20px;
height: 20px;
border-width: 2px;
margin: 0 auto;
}
/* as a big ol' click target, don't let text inside be selected */
@include unselectable;
}
// Search
// Categories
&#user-dropdown {
width: 118px;
}
.btn {
padding: 2px 8px;
margin-bottom: 2px;
.fa {
margin-right: 5px;
}
}
}
.highlight-strong {
background-color: dark-light-diff($highlight, $secondary, 40%, -45%);

View File

@ -52,8 +52,8 @@
}
.hamburger-panel {
ul.location-links li, li.heading {
.menu-panel {
ul.menu-links li, ul li.heading {
a {
padding: 0.5em;
display: block;
@ -74,6 +74,7 @@
background-color: transparent;
vertical-align: top;
padding: 5px 5px 2px 5px;
display: inline;
}
}
@ -171,3 +172,58 @@
button {margin-left: 5px;}
}
}
.user-menu {
.notifications {
h3 {
padding: 0 0.4em;
font-weight: bold;
margin: 0.5em 0;
}
.fa { color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%)); }
.icon { color: dark-light-choose(scale-color($primary, $lightness: 30%), scale-color($secondary, $lightness: 70%)); }
li {
background-color: dark-light-diff($tertiary, $secondary, 85%, -65%);
padding: 0.5em;
i {
float: left;
margin-right: 5px;
padding-top: 2px;
}
span { color: $primary; }
&:hover { background-color: dark-light-diff($highlight, $secondary, 50%, -55%); }
a { padding: 0; }
p {
margin: 0;
overflow: hidden;
}
}
.is-warning {
i.fa-envelope-o {
&:before {
content: "\f0e0";
}
color: $danger;
}
}
.read {
background-color: $secondary;
}
.none {
padding-top: 5px;
}
.spinner-container.visible {
min-height: 30px;
}
.spinner {
width: 20px;
height: 20px;
border-width: 2px;
margin: 0 auto;
}
/* as a big ol' click target, don't let text inside be selected */
@include unselectable;
}
}

View File

@ -132,7 +132,7 @@
overflow: hidden;
text-overflow: ellipsis;
.d-dropdown & {
.menu-panel & {
max-width: 90px;
}
}

View File

@ -26,29 +26,6 @@ and (max-width : 570px) {
padding-top: 82px;
}
// Dropdowns
// --------------------------------------------------
.d-dropdown {
width: 320px;
overflow: auto;
// Common
.heading {
border-top: 1px solid dark-light-diff($primary, $secondary, 90%, -60%);
}
// Categories
.btn {
padding: 2px 8px;
margin-bottom: 2px;
.fa {
margin-right: 5px;
}
}
}
.search-link .blurb {
color: dark-light-choose(scale-color($primary, $lightness: 45%), scale-color($secondary, $lightness: 55%));
display: block;
@ -59,16 +36,3 @@ and (max-width : 570px) {
color: dark-light-choose(scale-color($primary, $lightness: 25%), scale-color($secondary, $lightness: 75%));
}
}
.d-dropdown#search-dropdown {
width: 540px;
}
.d-dropdown .searching {
right: 30px;
}
#search-dropdown .results {
max-height: 500px;
}

View File

@ -37,48 +37,6 @@
padding-top: 60px;
}
// Dropdowns
// --------------------------------------------------
.d-dropdown {
width: 290px;
margin-top: -1px;
right: -$mobile-wrapper-padding; // Line-up with edge of screen, not edge of padding
// Common
.heading {
color: $primary;
font-weight: bold;
font-size: 0.857em;
line-height: 15px;
}
// Search
.searching {
position: absolute;
top: 0;
right: 25px;
}
input[type='text'] {
width: 265px;
font-size: 1.143em;
}
&#user-dropdown {
width: 155px;
}
.btn {
padding: 2px 8px;
.fa {
margin-right: 5px;
}
}
}
.search-link .badge-category {
display: none;
}
@ -86,7 +44,3 @@
.search-link .topic-statuses .topic-status i {
font-size: 1em;
}
.d-dropdown#search-dropdown .heading {
padding: 0;
}

View File

@ -5,18 +5,10 @@ acceptance("Header (Staff)", { loggedIn: true });
test("header", () => {
visit("/");
// Notifications
click("#user-notifications");
andThen(() => {
var $items = $("#notifications-dropdown li");
ok(exists($items), "is lazily populated after user opens it");
ok($items.first().hasClass("read"), "correctly binds items' 'read' class");
});
// User dropdown
click("#current-user");
andThen(() => {
ok(exists("#user-dropdown:visible"), "is lazily rendered after user opens it");
ok(exists("#user-dropdown .user-dropdown-links"), "has showing / hiding user-dropdown links correctly bound");
ok(exists(".user-menu:visible"), "is lazily rendered after user opens it");
ok(exists(".user-menu .menu-links"), "has showing / hiding user-dropdown links correctly bound");
});
});

View File

@ -18,19 +18,16 @@ componentTest('as a dropdown', {
test(assert) {
assert.ok(exists(".menu-panel.hidden"), "hidden by default");
assert.ok(!exists(".menu-selected.active"), "does not mark anything as active");
this.set('panelVisible', true);
andThen(() => {
assert.ok(!exists('.menu-panel .close-panel'), "the close X is not shown");
assert.ok(!exists(".menu-panel.hidden"), "toggling visible makes it appear");
assert.ok(exists(".menu-selected.active"), "marks the panel as active");
});
click('#outside-area')
andThen(() => {
assert.ok(exists(".menu-panel.hidden"), "clicking the body hides the menu");
assert.ok(!exists(".menu-selected.active"), "removes the active class");
assert.equal(this.get('panelVisible'), false, 'it updates the bound variable');
});
}
@ -52,12 +49,10 @@ componentTest('as a slide-in', {
test(assert) {
assert.ok(exists(".menu-panel.hidden"), "hidden by default");
assert.ok(!exists(".menu-selected.active"), "does not mark anything as active");
this.set('panelVisible', true);
andThen(() => {
assert.ok(!exists(".menu-panel.hidden"), "toggling visible makes it appear");
assert.ok(!exists(".menu-selected.active"), "slide ins don't mark as active");
});
click('#outside-area')

View File

@ -2,7 +2,7 @@ import { blank, present } from 'helpers/qunit-helpers';
moduleFor('controller:topic', 'controller:topic', {
needs: ['controller:header', 'controller:modal', 'controller:composer', 'controller:quote-button',
'controller:search', 'controller:topic-progress', 'controller:application']
'controller:topic-progress', 'controller:application']
});
import Topic from 'discourse/models/topic';

View File

@ -1,10 +0,0 @@
moduleFor("controller:user-dropdown");
test("showAdminLinks", function() {
const currentUser = Ember.Object.create({ staff: true });
const controller = this.subject({ currentUser });
equal(controller.get("showAdminLinks"), true, "is true when current user is a staff member");
currentUser.set("staff", false);
equal(controller.get("showAdminLinks"), false, "is false when current user is not a staff member");
});