mirror of
https://github.com/discourse/discourse.git
synced 2024-11-30 18:55:27 +08:00
Add Global Keyboard Shortcuts
Not all of these have been fully implemented yet. **Jump To** * `g` then `h` - Home (Latest) * `g` then `l` - Latest * `g` then `n` - New * `g` then `u` - Unread * `g` then `f` - Favorited * `g` then `c` - Categories List **Navigation** * `u` - Back to topic list * `k` / `j` - Newer/Older conversation or post * `o` or `Enter` - Open selected conversation * <code>`</code> - Go to next section * `~` - Go to previous section **Application** * `c` - Create a new topic * `n` - Open notifications menu * `/` - Search * `?` - Open keyboard shortcut help **Actions** * `f` - Favorite topic * `s` - Share topic * `<Shift>` + `s` - Share selected post * `r` - Reply to topic * `<Shift>` + `r` - Reply to selected post * `l` - Like selected post * `!` - Flag selected post * `b` - Bookmark selected post * `e` - Edit selected post * `d` - Delete selected post * `m` then `m` - Mark topic as muted * `m` then `r` - Mark topic as regular * `m` then `t` - Mark topic as tracking * `m` then `w` - Mark topic as watching
This commit is contained in:
parent
e44f51f9fa
commit
5100c2bbd2
|
@ -0,0 +1,145 @@
|
||||||
|
/**
|
||||||
|
Keyboard Shortcut related functions.
|
||||||
|
|
||||||
|
@class KeyboardShortcuts
|
||||||
|
@namespace Discourse
|
||||||
|
@module Discourse
|
||||||
|
**/
|
||||||
|
Discourse.KeyboardShortcuts = Ember.Object.createWithMixins({
|
||||||
|
PATH_BINDINGS: {
|
||||||
|
'g h': '/',
|
||||||
|
'g l': '/latest',
|
||||||
|
'g n': '/new',
|
||||||
|
'g u': '/unread',
|
||||||
|
'g f': '/favorited',
|
||||||
|
'g c': '/categories'
|
||||||
|
},
|
||||||
|
|
||||||
|
CLICK_BINDINGS: {
|
||||||
|
'b': 'article.selected button.bookmark', // bookmark current post
|
||||||
|
'c': '#create-topic', // create new topic
|
||||||
|
'd': 'article.selected button.delete', // delete selected post
|
||||||
|
'e': 'article.selected button.edit', // edit selected post
|
||||||
|
|
||||||
|
// favorite topic
|
||||||
|
'f': '#topic-footer-buttons button.favorite, #topic-list tr.topic-list-item.selected a.star',
|
||||||
|
|
||||||
|
'l': 'article.selected button.like', // like selected post
|
||||||
|
'm m': 'div.notification-options li[data-id="0"] a', // mark topic as muted
|
||||||
|
'm r': 'div.notification-options li[data-id="1"] a', // mark topic as regular
|
||||||
|
'm t': 'div.notification-options li[data-id="2"] a', // mark topic as tracking
|
||||||
|
'm w': 'div.notification-options li[data-id="3"] a', // mark topic as watching
|
||||||
|
'n': '#user-notifications', // open notifictions menu
|
||||||
|
'o,enter': '#topic-list tr.topic-list-item.selected a.title', // open selected topic
|
||||||
|
'r': '#topic-footer-buttons button.create', // reply to topic
|
||||||
|
'R': 'article.selected button.create', // reply to selected post
|
||||||
|
's': '#topic-footer-buttons button.share', // share topic
|
||||||
|
'S': 'article.selected button.share', // share selected post
|
||||||
|
'/': '#search-button', // focus search
|
||||||
|
'!': 'article.selected button.flag', // flag selected post
|
||||||
|
'?': '#keyboard-help' // open keyboard shortcut help
|
||||||
|
},
|
||||||
|
|
||||||
|
FUNCTION_BINDINGS: {
|
||||||
|
'j': 'selectDown',
|
||||||
|
'k': 'selectUp',
|
||||||
|
'u': 'goBack',
|
||||||
|
'`': 'nextSection',
|
||||||
|
'~': 'prevSection'
|
||||||
|
},
|
||||||
|
|
||||||
|
bindEvents: function(keyTrapper) {
|
||||||
|
this.keyTrapper = keyTrapper;
|
||||||
|
_.each(this.PATH_BINDINGS, this._bindToPath, this);
|
||||||
|
_.each(this.CLICK_BINDINGS, this._bindToClick, this);
|
||||||
|
_.each(this.FUNCTION_BINDINGS, this._bindToFunction, this);
|
||||||
|
},
|
||||||
|
|
||||||
|
selectDown: function() {
|
||||||
|
this._moveSelection(1);
|
||||||
|
},
|
||||||
|
|
||||||
|
selectUp: function() {
|
||||||
|
this._moveSelection(-1);
|
||||||
|
},
|
||||||
|
|
||||||
|
goBack: function() {
|
||||||
|
history.back();
|
||||||
|
},
|
||||||
|
|
||||||
|
nextSection: function() {
|
||||||
|
this._changeSection(1);
|
||||||
|
},
|
||||||
|
|
||||||
|
prevSection: function() {
|
||||||
|
this._changeSection(-1);
|
||||||
|
},
|
||||||
|
|
||||||
|
_bindToPath: function(path, binding) {
|
||||||
|
this.keyTrapper.bind(binding, function() {
|
||||||
|
Discourse.URL.routeTo(path);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
_bindToClick: function(selector, binding) {
|
||||||
|
binding = binding.split(',');
|
||||||
|
this.keyTrapper.bind(binding, function(e) {
|
||||||
|
if (!_.isUndefined(e) && _.isFunction(e.preventDefault)) {
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
$(selector).click();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
_bindToFunction: function(func, binding) {
|
||||||
|
if (typeof this[func] === 'function') {
|
||||||
|
this.keyTrapper.bind(binding, _.bind(this[func], this));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_moveSelection: function(num) {
|
||||||
|
var $articles = this._findArticles();
|
||||||
|
|
||||||
|
if (typeof $articles === 'undefined') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var $selected = $articles.filter('.selected'),
|
||||||
|
index = $articles.index($selected),
|
||||||
|
$article = $articles.eq(index + num);
|
||||||
|
|
||||||
|
if ($article.size() > 0) {
|
||||||
|
$articles.removeClass('selected');
|
||||||
|
$article.addClass('selected');
|
||||||
|
this._scrollList($article);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_scrollList: function($article) {
|
||||||
|
var $body = $('body'),
|
||||||
|
distToElement = $article.position().top + $article.height() - $(window).height() - $body.scrollTop();
|
||||||
|
|
||||||
|
$('html, body').scrollTop($body.scrollTop() + distToElement);
|
||||||
|
},
|
||||||
|
|
||||||
|
_findArticles: function() {
|
||||||
|
var $topicList = $('#topic-list'),
|
||||||
|
$topicArea = $('.posts-wrapper');
|
||||||
|
|
||||||
|
if ($topicArea.size() > 0) {
|
||||||
|
return $topicArea.find('.topic-post');
|
||||||
|
}
|
||||||
|
else if ($topicList.size() > 0) {
|
||||||
|
return $topicList.find('.topic-list-item');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_changeSection: function(num) {
|
||||||
|
var $sections = $('#category-filter').find('li'),
|
||||||
|
$active = $sections.filter('.active'),
|
||||||
|
index = $sections.index('.active');
|
||||||
|
|
||||||
|
$sections.eq(index + num).find('a').click();
|
||||||
|
}
|
||||||
|
});
|
|
@ -0,0 +1,12 @@
|
||||||
|
/**
|
||||||
|
This controller is used to display the Keyboard Shortcuts Help Modal
|
||||||
|
|
||||||
|
@class KeyboardShortcutsHelpController
|
||||||
|
@extends Discourse.Controller
|
||||||
|
@namespace Discourse
|
||||||
|
@uses Discourse.ModalFunctionality
|
||||||
|
@module Discourse
|
||||||
|
**/
|
||||||
|
Discourse.KeyboardShortcutsHelpController = Discourse.Controller.extend(Discourse.ModalFunctionality, {
|
||||||
|
needs: ['modal']
|
||||||
|
});
|
|
@ -0,0 +1,8 @@
|
||||||
|
/*global Mousetrap:true*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
Initialize Global Keyboard Shortcuts
|
||||||
|
**/
|
||||||
|
Discourse.addInitializer(function(){
|
||||||
|
Discourse.KeyboardShortcuts.bindEvents(Mousetrap);
|
||||||
|
})
|
|
@ -31,6 +31,10 @@ Discourse.ApplicationRoute = Em.Route.extend({
|
||||||
this.controllerFor('uploadSelector').setProperties({ composerView: composerView });
|
this.controllerFor('uploadSelector').setProperties({ composerView: composerView });
|
||||||
},
|
},
|
||||||
|
|
||||||
|
showKeyboardShortcutsHelp: function() {
|
||||||
|
Discourse.Route.showModal(this, 'keyboardShortcutsHelp');
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Close the current modal, and destroy its state.
|
Close the current modal, and destroy its state.
|
||||||
|
|
|
@ -52,11 +52,12 @@
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
{{#if Discourse.loginRequired}}
|
{{#if Discourse.loginRequired}}
|
||||||
<a class='icon expand' href='#' {{action showLogin}}>
|
<a id='search-button' class='icon expand' href='#' {{action showLogin}}>
|
||||||
<i class='fa fa-search'></i>
|
<i class='fa fa-search'></i>
|
||||||
</a>
|
</a>
|
||||||
{{else}}
|
{{else}}
|
||||||
<a class='icon expand'
|
<a id='search-button'
|
||||||
|
class='icon expand'
|
||||||
href='#'
|
href='#'
|
||||||
data-dropdown="search-dropdown"
|
data-dropdown="search-dropdown"
|
||||||
title='{{i18n search.title}}'>
|
title='{{i18n search.title}}'>
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
{{#if canCreateTopic}}
|
{{#if canCreateTopic}}
|
||||||
<button class='btn btn-default' {{action createTopic}}><i class='fa fa-plus'></i>{{i18n topic.create}}</button>
|
<button id="create-topic" class='btn btn-default' {{action createTopic}}><i class='fa fa-plus'></i>{{i18n topic.create}}</button>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
{{#if canEditCategory}}
|
{{#if canEditCategory}}
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
<div id="keyboard-shortcuts-help" class="modal-body">
|
||||||
|
<div class="row">
|
||||||
|
<div class="span6">
|
||||||
|
<h4>{{i18n keyboard_shortcuts_help.jump_to.title}}</h4>
|
||||||
|
<ul>
|
||||||
|
<li>{{{i18n keyboard_shortcuts_help.jump_to.home}}}</li>
|
||||||
|
<li>{{{i18n keyboard_shortcuts_help.jump_to.latest}}}</li>
|
||||||
|
<li>{{{i18n keyboard_shortcuts_help.jump_to.new}}}</li>
|
||||||
|
<li>{{{i18n keyboard_shortcuts_help.jump_to.unread}}}</li>
|
||||||
|
<li>{{{i18n keyboard_shortcuts_help.jump_to.favorited}}}</li>
|
||||||
|
<li>{{{i18n keyboard_shortcuts_help.jump_to.categories}}}</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h4>{{i18n keyboard_shortcuts_help.navigation.title}}</h4>
|
||||||
|
<ul>
|
||||||
|
<li>{{{i18n keyboard_shortcuts_help.navigation.back}}}</li>
|
||||||
|
<li>{{{i18n keyboard_shortcuts_help.navigation.up_down}}}</li>
|
||||||
|
<li>{{{i18n keyboard_shortcuts_help.navigation.open}}}</li>
|
||||||
|
<li>{{{i18n keyboard_shortcuts_help.navigation.next_prev}}}</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h4>{{i18n keyboard_shortcuts_help.application.title}}</h4>
|
||||||
|
<ul>
|
||||||
|
<li>{{{i18n keyboard_shortcuts_help.application.create}}}</li>
|
||||||
|
<li>{{{i18n keyboard_shortcuts_help.application.notifications}}}</li>
|
||||||
|
<li>{{{i18n keyboard_shortcuts_help.application.search}}}</li>
|
||||||
|
<li>{{{i18n keyboard_shortcuts_help.application.help}}}</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="span6">
|
||||||
|
<h4>{{i18n keyboard_shortcuts_help.actions.title}}</h4>
|
||||||
|
<ul>
|
||||||
|
<li>{{{i18n keyboard_shortcuts_help.actions.favorite}}}</li>
|
||||||
|
<li>{{{i18n keyboard_shortcuts_help.actions.share_topic}}}</li>
|
||||||
|
<li>{{{i18n keyboard_shortcuts_help.actions.share_post}}}</li>
|
||||||
|
<li>{{{i18n keyboard_shortcuts_help.actions.reply_topic}}}</li>
|
||||||
|
<li>{{{i18n keyboard_shortcuts_help.actions.reply_post}}}</li>
|
||||||
|
<li>{{{i18n keyboard_shortcuts_help.actions.like}}}</li>
|
||||||
|
<li>{{{i18n keyboard_shortcuts_help.actions.flag}}}</li>
|
||||||
|
<li>{{{i18n keyboard_shortcuts_help.actions.bookmark}}}</li>
|
||||||
|
<li>{{{i18n keyboard_shortcuts_help.actions.edit}}}</li>
|
||||||
|
<li>{{{i18n keyboard_shortcuts_help.actions.delete}}}</li>
|
||||||
|
<li>{{{i18n keyboard_shortcuts_help.actions.mark_muted}}}</li>
|
||||||
|
<li>{{{i18n keyboard_shortcuts_help.actions.mark_regular}}}</li>
|
||||||
|
<li>{{{i18n keyboard_shortcuts_help.actions.mark_tracking}}}</li>
|
||||||
|
<li>{{{i18n keyboard_shortcuts_help.actions.mark_watching}}}</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -6,6 +6,7 @@
|
||||||
{{/if}}
|
{{/if}}
|
||||||
<li>{{partial "siteMap/latestTopicsLink"}}</li>
|
<li>{{partial "siteMap/latestTopicsLink"}}</li>
|
||||||
<li>{{partial "siteMap/faqLink"}}</li>
|
<li>{{partial "siteMap/faqLink"}}</li>
|
||||||
|
<li>{{partial "siteMap/keyboardShortcutsHelpLink"}}</li>
|
||||||
{{#if showMobileToggle}}
|
{{#if showMobileToggle}}
|
||||||
<li>{{partial "siteMap/mobileToggleLink"}}</li>
|
<li>{{partial "siteMap/mobileToggleLink"}}</li>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
<a id="keyboard-help" {{action showKeyboardShortcutsHelp}}>{{i18n keyboard_shortcuts_help.title}}</a>
|
|
@ -7,6 +7,7 @@
|
||||||
@module Discourse
|
@module Discourse
|
||||||
**/
|
**/
|
||||||
Discourse.FavoriteButton = Discourse.ButtonView.extend({
|
Discourse.FavoriteButton = Discourse.ButtonView.extend({
|
||||||
|
classNames: ['favorite'],
|
||||||
textKey: 'favorite.title',
|
textKey: 'favorite.title',
|
||||||
helpKeyBinding: 'controller.favoriteTooltipKey',
|
helpKeyBinding: 'controller.favoriteTooltipKey',
|
||||||
attributeBindings: ['disabled'],
|
attributeBindings: ['disabled'],
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
@module Discourse
|
@module Discourse
|
||||||
**/
|
**/
|
||||||
Discourse.NotificationsButton = Discourse.DropdownButtonView.extend({
|
Discourse.NotificationsButton = Discourse.DropdownButtonView.extend({
|
||||||
|
classNames: ['notification-options'],
|
||||||
title: I18n.t('topic.notifications.title'),
|
title: I18n.t('topic.notifications.title'),
|
||||||
longDescriptionBinding: 'topic.details.notificationReasonText',
|
longDescriptionBinding: 'topic.details.notificationReasonText',
|
||||||
topic: Em.computed.alias('controller.model'),
|
topic: Em.computed.alias('controller.model'),
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
@module Discourse
|
@module Discourse
|
||||||
**/
|
**/
|
||||||
Discourse.ShareButton = Discourse.ButtonView.extend({
|
Discourse.ShareButton = Discourse.ButtonView.extend({
|
||||||
|
classNames: ['share'],
|
||||||
textKey: 'topic.share.title',
|
textKey: 'topic.share.title',
|
||||||
helpKey: 'topic.share.help',
|
helpKey: 'topic.share.help',
|
||||||
'data-share-url': Em.computed.alias('topic.shareUrl'),
|
'data-share-url': Em.computed.alias('topic.shareUrl'),
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
/**
|
||||||
|
A modal view for displaying Keyboard Shortcut Help
|
||||||
|
|
||||||
|
@class KeyboardShortcutsHelpView
|
||||||
|
@extends Discourse.ModalBodyView
|
||||||
|
@namespace Discourse
|
||||||
|
@module Discourse
|
||||||
|
**/
|
||||||
|
Discourse.KeyboardShortcutsHelpView = Discourse.ModalBodyView.extend({
|
||||||
|
templateName: 'modal/keyboard_shortcuts_help',
|
||||||
|
title: I18n.t('keyboard_shortcuts_help.title')
|
||||||
|
});
|
|
@ -0,0 +1,33 @@
|
||||||
|
@import "../foundation/variables";
|
||||||
|
|
||||||
|
.topic-list-item td:first-child, .topic-post {
|
||||||
|
background-color: inherit;
|
||||||
|
border-left: 1px solid transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.topic-list-item.selected td:first-child, .topic-post.selected {
|
||||||
|
border-left: 1px solid $topic-list-starred-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
.topic-list-item.selected {
|
||||||
|
background-color: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
#keyboard-shortcuts-help {
|
||||||
|
ul {
|
||||||
|
list-style: none;
|
||||||
|
padding-left: 0;
|
||||||
|
|
||||||
|
li {
|
||||||
|
margin: 5px 0px
|
||||||
|
}
|
||||||
|
|
||||||
|
b {
|
||||||
|
background-color: $nav-stacked-divider-color;
|
||||||
|
color: $nav-stacked-color;
|
||||||
|
display: inline-block;
|
||||||
|
margin: 0px 2px;
|
||||||
|
padding: 2px 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -244,7 +244,7 @@ en:
|
||||||
|
|
||||||
change_username:
|
change_username:
|
||||||
title: "Change Username"
|
title: "Change Username"
|
||||||
confirm: "If you change your username, all prior quotes of your posts and @name mentions will be broken. Are you absolutely sure you want to?"
|
confirm: "I you change your username, all prior quotes of your posts and @name mentions will be broken. Are you absolutely sure you want to?"
|
||||||
taken: "Sorry, that username is taken."
|
taken: "Sorry, that username is taken."
|
||||||
error: "There was an error changing your username."
|
error: "There was an error changing your username."
|
||||||
invalid: "That username is invalid. It must only include numbers and letters"
|
invalid: "That username is invalid. It must only include numbers and letters"
|
||||||
|
@ -1468,3 +1468,42 @@ en:
|
||||||
|
|
||||||
lightbox:
|
lightbox:
|
||||||
download: "download"
|
download: "download"
|
||||||
|
|
||||||
|
keyboard_shortcuts_help:
|
||||||
|
title: 'Keyboard Shortcuts'
|
||||||
|
jump_to:
|
||||||
|
title: 'Jump To'
|
||||||
|
home: '<b>g</b> then <b>h</b> Home (Latest)'
|
||||||
|
latest: '<b>g</b> then <b>l</b> Latest'
|
||||||
|
new: '<b>g</b> then <b>n</b> New'
|
||||||
|
unread: '<b>g</b> then <b>u</b> Unread'
|
||||||
|
favorited: '<b>g</b> then <b>f</b> Favorited'
|
||||||
|
categories: '<b>g</b> then <b>c</b> Categories'
|
||||||
|
navigation:
|
||||||
|
title: 'Navigation'
|
||||||
|
back: '<b>u</b> Back'
|
||||||
|
up_down: '<b>k</b>/<b>j</b> Move selection up/down'
|
||||||
|
open: '<b>o</b> or <b>Enter</b> Open selected topic'
|
||||||
|
next_prev: '<b>`</b>/<b>~</b> Next/previous section'
|
||||||
|
application:
|
||||||
|
title: 'Application'
|
||||||
|
create: '<b>c</b> Create a new topic'
|
||||||
|
notifications: '<b>n</b> Open notifications'
|
||||||
|
search: '<b>/</b> Search'
|
||||||
|
help: '<b>?</b> Open keyboard shortcuts help'
|
||||||
|
actions:
|
||||||
|
title: 'Actions'
|
||||||
|
favorite: '<b>f</b> Favorite topic'
|
||||||
|
share_topic: '<b>s</b> Share topic'
|
||||||
|
share_post: '<b>shift s</b> Share post'
|
||||||
|
reply_topic: '<b>r</b> Reply to topic'
|
||||||
|
reply_post: '<b>shift r</b> Reply to post'
|
||||||
|
like: '<b>l</b> Like post'
|
||||||
|
flag: '<b>!</b> Flag post'
|
||||||
|
bookmark: '<b>b</b> Bookmark post'
|
||||||
|
edit: '<b>e</b> Edit post'
|
||||||
|
delete: '<b>d</b> Delete post'
|
||||||
|
mark_muted: '<b>m</b> then <b>m</b> Mark topic as muted'
|
||||||
|
mark_regular: '<b>m</b> then <b>r</b> Mark topic as regular'
|
||||||
|
mark_tracking: '<b>m</b> then <b>t</b> Mark topic as tracking'
|
||||||
|
mark_watching: '<b>m</b> then <b>w</b> Mark topic as watching'
|
||||||
|
|
157
test/javascripts/components/keyboard_shortcuts_component_test.js
Normal file
157
test/javascripts/components/keyboard_shortcuts_component_test.js
Normal file
|
@ -0,0 +1,157 @@
|
||||||
|
module("Discourse.KeyboardShortcuts", {
|
||||||
|
setup: function() {
|
||||||
|
this.testMouseTrap = {
|
||||||
|
bindings: {},
|
||||||
|
|
||||||
|
bind: function(bindings, callback) {
|
||||||
|
var registerBinding = _.bind(function(binding) {
|
||||||
|
this.bindings[binding] = callback;
|
||||||
|
}, this);
|
||||||
|
|
||||||
|
if (_.isArray(bindings)) {
|
||||||
|
_.each(bindings, registerBinding, this);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
registerBinding(bindings);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
trigger: function(binding) {
|
||||||
|
this.bindings[binding].call();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
sinon.stub(Discourse.URL, "routeTo");
|
||||||
|
|
||||||
|
$("#qunit-fixture").html([
|
||||||
|
"<article class='selected'>",
|
||||||
|
" <button class='bookmark'></button>",
|
||||||
|
" <button class='delete'></button>",
|
||||||
|
" <button class='edit'></button>",
|
||||||
|
" <button class='like'></button>",
|
||||||
|
" <button class='create'></button>",
|
||||||
|
" <button class='share'></button>",
|
||||||
|
" <button class='flag'></button>",
|
||||||
|
"</article>",
|
||||||
|
"<div class='notification-options'>",
|
||||||
|
" <ul>",
|
||||||
|
" <li data-id='0'><a></a></li>",
|
||||||
|
" <li data-id='1'><a></a></li>",
|
||||||
|
" <li data-id='2'><a></a></li>",
|
||||||
|
" <li data-id='3'><a></a></li>",
|
||||||
|
" </ul>",
|
||||||
|
"</div>",
|
||||||
|
"<table id='topic-list'>",
|
||||||
|
" <tr class='topic-list-item selected'><td>",
|
||||||
|
" <a class='title'></a>",
|
||||||
|
" </td></tr>",
|
||||||
|
"</table>",
|
||||||
|
"<div id='topic-footer-buttons'>",
|
||||||
|
" <button class='favorite'></button>",
|
||||||
|
" <button class='create'></button>",
|
||||||
|
" <button class='share'></button>",
|
||||||
|
"</div>",
|
||||||
|
"<button id='create-topic'></button>",
|
||||||
|
"<div id='user-notifications'></div>",
|
||||||
|
"<div id='search-button'></div>",
|
||||||
|
"<div id='keyboard-help'></div>"
|
||||||
|
].join("\n"));
|
||||||
|
},
|
||||||
|
|
||||||
|
teardown: function() {
|
||||||
|
$("#qunit-scratch").html("");
|
||||||
|
|
||||||
|
Discourse.URL.routeTo.restore();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var pathBindings = Discourse.KeyboardShortcuts.PATH_BINDINGS;
|
||||||
|
|
||||||
|
_.each(pathBindings, function(path, binding) {
|
||||||
|
var testName = binding + " goes to " + path;
|
||||||
|
|
||||||
|
test(testName, function() {
|
||||||
|
Discourse.KeyboardShortcuts.bindEvents(this.testMouseTrap);
|
||||||
|
this.testMouseTrap.trigger(binding);
|
||||||
|
|
||||||
|
ok(Discourse.URL.routeTo.calledWith(path));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
var clickBindings = Discourse.KeyboardShortcuts.CLICK_BINDINGS;
|
||||||
|
|
||||||
|
_.each(clickBindings, function(selector, binding) {
|
||||||
|
var bindings = binding.split(",");
|
||||||
|
|
||||||
|
var testName = binding + " clicks on " + selector;
|
||||||
|
|
||||||
|
test(testName, bindings.length, function() {
|
||||||
|
Discourse.KeyboardShortcuts.bindEvents(this.testMouseTrap);
|
||||||
|
$(selector).on("click", function() {
|
||||||
|
ok(true, selector + " was clicked");
|
||||||
|
});
|
||||||
|
|
||||||
|
_.each(bindings, function(binding) {
|
||||||
|
this.testMouseTrap.trigger(binding);
|
||||||
|
}, this);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
var functionBindings = Discourse.KeyboardShortcuts.FUNCTION_BINDINGS;
|
||||||
|
|
||||||
|
_.each(functionBindings, function(func, binding) {
|
||||||
|
var testName = binding + " calls " + func;
|
||||||
|
|
||||||
|
test(testName, function() {
|
||||||
|
var stub = sinon.stub(Discourse.KeyboardShortcuts, func, function() {
|
||||||
|
ok(true, func + " is called when " + binding + " is triggered");
|
||||||
|
});
|
||||||
|
Discourse.KeyboardShortcuts.bindEvents(this.testMouseTrap);
|
||||||
|
|
||||||
|
this.testMouseTrap.trigger(binding);
|
||||||
|
stub.restore();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test("selectDown calls _moveSelection with 1", function() {
|
||||||
|
var spy = sinon.spy(Discourse.KeyboardShortcuts, '_moveSelection');
|
||||||
|
|
||||||
|
Discourse.KeyboardShortcuts.selectDown();
|
||||||
|
ok(spy.calledWith(1), "_moveSelection is called with 1");
|
||||||
|
spy.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("selectUp calls _moveSelection with -1", function() {
|
||||||
|
var spy = sinon.spy(Discourse.KeyboardShortcuts, '_moveSelection');
|
||||||
|
|
||||||
|
Discourse.KeyboardShortcuts.selectUp();
|
||||||
|
ok(spy.calledWith(-1), "_moveSelection is called with -1");
|
||||||
|
spy.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("goBack calls history.back", function() {
|
||||||
|
var called = false,
|
||||||
|
stub = sinon.stub(history, 'back', function() {
|
||||||
|
called = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
Discourse.KeyboardShortcuts.goBack();
|
||||||
|
ok(called, "history.back is called");
|
||||||
|
stub.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("nextSection calls _changeSection with 1", function() {
|
||||||
|
var spy = sinon.spy(Discourse.KeyboardShortcuts, '_changeSection');
|
||||||
|
|
||||||
|
Discourse.KeyboardShortcuts.nextSection();
|
||||||
|
ok(spy.calledWith(1), "_changeSection is called with 1");
|
||||||
|
spy.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("prevSection calls _changeSection with -1", function() {
|
||||||
|
var spy = sinon.spy(Discourse.KeyboardShortcuts, '_changeSection');
|
||||||
|
|
||||||
|
Discourse.KeyboardShortcuts.prevSection();
|
||||||
|
ok(spy.calledWith(-1), "_changeSection is called with -1");
|
||||||
|
spy.restore();
|
||||||
|
});
|
Loading…
Reference in New Issue
Block a user