buddy list: Fix and simplify up/down navigation.

This introduces a generic class called list_cursor to handle the
main details of navigating the buddy list and wires it into
activity.js.  It replaces some fairly complicated code that
was coupled to stream_list and used lots of jQuery.

The new code interacts with the buddy_list API instead of jQuery
directly.  It also persists the key across redraws, so we don't
lose our place when a focus ping happens or we type more characters.

Note that we no longer cycle to the top when we hit the bottom, or
vice versa.  Cycling can be kind of an anti-feature when you want to
just lay on the arrow keys until they hit the end.

The changes to stream_list.js here do not affect the left sidebar;
they only remove code that was used for the right sidebar.
This commit is contained in:
Steve Howell 2018-04-21 12:59:03 +00:00 committed by Tim Abbott
parent c63f2db25b
commit fb712027bf
11 changed files with 401 additions and 224 deletions

View File

@ -146,6 +146,7 @@
"user_search": false,
"buddy_data": false,
"buddy_list": false,
"list_cursor": false,
"activity": false,
"invite": false,
"colorspace": false,

View File

@ -470,22 +470,23 @@ casper.waitForSelector('#user_presences .highlighted_user', function () {
// Use arrow keys to navigate through suggestions
casper.then(function () {
// Down: Cordelia -> Hamlet
casper.sendKeys('.user-list-filter', casper.page.event.key.Down, {keepFocus: true});
// Up: Hamlet -> Cordelia
casper.sendKeys('.user-list-filter', casper.page.event.key.Up, {keepFocus: true});
// Up: Cordelia -> aaron
casper.sendKeys('.user-list-filter', casper.page.event.key.Up, {keepFocus: true});
function arrow(key) {
casper.sendKeys('.user-list-filter',
casper.page.event.key[key],
{keepFocus: true});
}
arrow('Down'); // Cordelia -> Hamlet
arrow('Up'); // Hamlet -> Cordelia
arrow('Up'); // already at top
arrow('Down'); // Cordelia -> Hamlet
});
casper.waitForSelector('#user_presences li.highlighted_user [data-name="aaron"]', function () {
casper.waitForSelector('#user_presences li.highlighted_user [data-name="King Hamlet"]', function () {
casper.test.info('Suggestion highlighting - after arrow key navigation');
casper.test.assertDoesntExist('#user_presences li.highlighted_user [data-name="Cordelia Lear"]',
'User Cordelia Lear not is selected');
casper.test.assertDoesntExist('#user_presences li.highlighted_user [data-name="King Hamlet"]',
'User King Hamlet is not selected');
casper.test.assertExist('#user_presences li.highlighted_user [data-name="aaron"]',
'User aaron is selected');
casper.test.assertExist('#user_presences li.highlighted_user [data-name="King Hamlet"]',
'User King Hamlet is selected');
});
common.then_log_out();

View File

@ -39,13 +39,24 @@ zrequire('people');
zrequire('buddy_data');
zrequire('buddy_list');
zrequire('user_search');
zrequire('list_cursor');
zrequire('activity');
zrequire('stream_list');
set_global('blueslip', {
log: () => {},
});
var filter_key_handlers;
set_global('keydown_util', {
handle: (opts) => {
filter_key_handlers = opts.handlers;
},
});
set_global('stream_list', {
scroll_element_into_container: () => {},
});
set_global('popovers', {
hide_all: function () {},
show_userlist_sidebar: function () {
@ -273,33 +284,17 @@ presence.presence_info[norbert.user_id] = { status: activity.ACTIVE };
presence.presence_info[zoe.user_id] = { status: activity.ACTIVE };
presence.presence_info[me.user_id] = { status: activity.ACTIVE };
const user_order = [fred.user_id, jill.user_id, norbert.user_id,
zoe.user_id, alice.user_id, mark.user_id];
const user_count = 6;
function reset_jquery() {
function reset_setup() {
set_global('$', global.make_zjquery());
activity.set_user_list_filter();
// Mock the jquery is func
$('.user-list-filter').is = function (sel) {
if (sel === ':focus') {
return $('.user-list-filter').is_focused();
}
};
// Mock the jquery first func
$('#user_presences li.user_sidebar_entry.narrow-filter').first = function () {
return $('li.user_sidebar_entry[data-user-id="' + user_order[0] + '"]');
};
$('#user_presences li.user_sidebar_entry.narrow-filter').last = function () {
return $('li.user_sidebar_entry[data-user-id="' + user_order[user_count - 1] + '"]');
};
activity.set_cursor_and_filter();
buddy_list.container = $('#user_presences');
const stub = $.create('first elem stub');
stub.first = () => [];
buddy_list.container.set_find_results('li.user_sidebar_entry', stub);
}
reset_jquery();
reset_setup();
(function test_presence_list_full_update() {
$('.user-list-filter').focus();
@ -355,15 +350,38 @@ reset_jquery();
]);
}());
function simulate_right_column_buddy_list() {
$('.user-list-filter').closest = function (selector) {
assert.equal(selector, ".app-main [class^='column-']");
return $.create('right-sidebar').addClass('column-right');
};
}
function simulate_left_column_buddy_list() {
$('.user-list-filter').closest = function (selector) {
assert.equal(selector, ".app-main [class^='column-']");
return $.create('left-sidebar').addClass('column-left');
};
}
function simulate_list_items(items) {
const list = {
length: items.length,
eq: (i) => items[i],
first: () => items[0] || {length: 0},
};
$('#user_presences').set_find_results('li.user_sidebar_entry', list);
_.each(items, (item, i) => {
item.next = () => items[i+1] || {length: 0};
item.prev = () => items[i-1] || {length: 0};
});
}
function buddy_list_add(user_id, stub) {
if (stub.attr) {
stub.attr('data-user-id', user_id);
}
const sel = `li.user_sidebar_entry[data-user-id='${user_id}']`;
$('#user_presences').set_find_results(sel, stub);
}
@ -418,108 +436,96 @@ function buddy_list_add(user_id, stub) {
assert.equal(value.text(), '');
}());
reset_jquery();
reset_setup();
(function test_key_input() {
let sel_index = 0;
// Returns which element is selected
$('#user_presences li.user_sidebar_entry.narrow-filter.highlighted_user')
.expectOne().attr = function () {
return user_order[sel_index];
(function test_handlers() {
// This is kind of weak coverage; we are mostly making sure that
// keys and clicks got mapped to functions that don't crash.
const alice_li = $.create('alice stub');
const fred_li = $.create('fred stub');
(function test_click_filter() {
const e = {
stopPropagation: () => {},
};
// Returns element before selected one
$('#user_presences li.user_sidebar_entry.narrow-filter.highlighted_user')
.expectOne().prev = function () {
if (sel_index === 0) {
// Top, no prev element
return $('div.no_user');
}
return $('li.user_sidebar_entry[data-user-id="' + user_order[sel_index-1] + '"]');
simulate_list_items([alice_li, fred_li]);
const handler = $('.user-list-filter').get_on_handler('focus');
handler(e);
simulate_list_items([]);
handler(e);
}());
(function test_click_header_filter() {
const e = {};
const handler = $('#userlist-header').get_on_handler('click');
simulate_right_column_buddy_list();
handler(e);
// and click again
handler(e);
}());
(function test_filter_keys() {
simulate_list_items([alice_li, fred_li]);
buddy_list_add(alice.user_id, alice_li);
buddy_list_add(fred.user_id, fred_li);
activity.user_cursor.go_to(alice.user_id);
filter_key_handlers.down_arrow();
filter_key_handlers.up_arrow();
filter_key_handlers.up_arrow();
filter_key_handlers.down_arrow();
filter_key_handlers.down_arrow();
simulate_list_items([]);
filter_key_handlers.down_arrow();
filter_key_handlers.up_arrow();
}());
(function test_enter_key() {
var narrowed;
narrow.by = (method, email) => {
assert.equal(email, 'alice@zulip.com');
narrowed = true;
};
// Returns element after selected one
$('#user_presences li.user_sidebar_entry.narrow-filter.highlighted_user')
.expectOne().next = function () {
if (sel_index === user_count - 1) {
// Bottom, no next element
return $('div.no_user');
}
return $('li.user_sidebar_entry[data-user-id="' + user_order[sel_index + 1] + '"]');
$('.user-list-filter').val('al');
buddy_list_add(alice.user_id, alice_li);
activity.user_cursor.go_to(alice.user_id);
filter_key_handlers.enter_key();
assert(narrowed);
// get line coverage for cleared case
activity.user_cursor.clear();
filter_key_handlers.enter_key();
}());
(function test_click_handler() {
// We wire up the click handler in click_handlers.js,
// so this just tests the called function.
var narrowed;
narrow.by = (method, email) => {
assert.equal(email, 'alice@zulip.com');
narrowed = true;
};
$('li.user_sidebar_entry[data-user-id="' + fred.user_id + '"]').is = function () {
return true;
};
$('li.user_sidebar_entry[data-user-id="' + mark.user_id + '"]').is = function () {
return true;
};
$('li.user_sidebar_entry[data-user-id="' + alice.user_id + '"]').is = function () {
return true;
};
$('div.no_user').is = function () {
return false;
};
buddy_list_add(alice.user_id, alice_li);
activity.narrow_for_user({li: alice_li});
assert(narrowed);
}());
$('#user_presences li.user_sidebar_entry.narrow-filter').length = user_count;
// Disable scrolling into place
stream_list.scroll_element_into_container = function () {};
// up
const e = {
keyCode: 38,
stopPropagation: function () {},
preventDefault: function () {},
};
const keydown_handler = $('.user-list-filter').get_on_handler('keydown');
keydown_handler(e);
// Now the last element is selected
sel_index = user_count - 1;
keydown_handler(e);
sel_index = sel_index - 1;
// down
e.keyCode = 40;
keydown_handler(e);
sel_index = sel_index + 1;
keydown_handler(e);
e.keyCode = 13;
// Enter text and narrow users
$(".user-list-filter").expectOne().val('ali');
narrow.by = function (method, email) {
assert.equal(email, 'alice@zulip.com');
};
compose_actions.start = function () {};
sel_index = 4;
keydown_handler(e);
}());
(function test_focus_user_filter() {
$('#user_presences li.user_sidebar_entry.narrow-filter.highlighted_user').length = 0;
var first_highlighted;
stream_list.highlight_first = () => {
first_highlighted = true;
};
const e = {
stopPropagation: () => {},
};
const handler = $('.user-list-filter').get_on_handler('click');
handler(e);
assert(first_highlighted);
}());
(function test_focusout_user_filter() {
const e = { };
const handler = $('.user-list-filter').get_on_handler('blur');
handler(e);
(function test_blur_filter() {
const e = {};
const handler = $('.user-list-filter').get_on_handler('blur');
handler(e);
}());
}());
presence.presence_info = {};
@ -530,12 +536,12 @@ presence.presence_info[mark.user_id] = { status: activity.IDLE };
presence.presence_info[norbert.user_id] = { status: activity.ACTIVE };
presence.presence_info[zoe.user_id] = { status: activity.ACTIVE };
reset_setup();
(function test_filter_user_ids() {
const user_filter = $('.user-list-filter');
user_filter.val(''); // no search filter
activity.set_user_list_filter();
function get_user_ids() {
var filter_text = activity.get_filter_text();
var user_ids = buddy_data.get_filtered_and_sorted_user_ids(filter_text);
@ -603,7 +609,7 @@ presence.presence_info[zoe.user_id] = { status: activity.ACTIVE };
assert(removed);
}());
reset_jquery();
reset_setup();
(function test_insert_fred_after_alice() {
const alice_li = $.create('alice list item');
@ -634,7 +640,7 @@ reset_jquery();
assert(removed);
}());
reset_jquery();
reset_setup();
(function test_insert_fred_before_jill() {
const fred_li = $.create('fred-li');
@ -666,7 +672,7 @@ reset_jquery();
}());
// Reset jquery here.
reset_jquery();
reset_setup();
(function test_insert_unfiltered_user_with_filter() {
// This test only tests that we do not explode when
@ -714,19 +720,21 @@ $('.user-list-filter').parent = function () {
assert($('#user-list .input-append').hasClass('notdisplayed'));
}());
reset_setup();
(function () {
const alice_li = $.create('alice stub');
simulate_list_items([alice_li]);
}());
(function test_initiate_search() {
$('.user-list-filter').blur();
$('.user-list-filter').closest = function (selector) {
assert.equal(selector, ".app-main [class^='column-']");
return $.create('right-sidebar').addClass('column-right');
};
simulate_right_column_buddy_list();
activity.initiate_search();
assert.equal($('.column-right').hasClass('expanded'), true);
assert.equal($('.user-list-filter').is_focused(), true);
$('.user-list-filter').closest = function (selector) {
assert.equal(selector, ".app-main [class^='column-']");
return $.create('left-sidebar').addClass('column-left');
};
simulate_left_column_buddy_list();
activity.initiate_search();
assert.equal($('.column-left').hasClass('expanded'), true);
assert.equal($('.user-list-filter').is_focused(), true);
@ -775,7 +783,7 @@ $('.user-list-filter').parent = function () {
activity.update_huddles = function () {};
}());
reset_jquery();
reset_setup();
(function test_set_user_status() {
const server_time = 500;

View File

@ -224,6 +224,9 @@ exports.make_new_elem = function (selector, opts) {
if (arg === ':visible') {
return shown;
}
if (arg === ':focus') {
return focused;
}
return self;
},
is_focused: function () {

View File

@ -224,12 +224,6 @@ exports.insert_user_into_list = function (user_id) {
compose_fade.update_one_user_row(elt);
};
exports.clear_highlight = function () {
// Undo highlighting
var item = $('#user_presences li.user_sidebar_entry.highlighted_user');
item.removeClass('highlighted_user');
};
exports.searching = function () {
return exports.user_filter && exports.user_filter.searching();
};
@ -252,16 +246,15 @@ exports.build_user_sidebar = function () {
resize.resize_page_components();
// Highlight top user when searching
$('#user_presences li.user_sidebar_entry.highlighted_user').removeClass('highlighted_user');
if (exports.searching()) {
var all_streams = $('#user_presences li.user_sidebar_entry.narrow-filter');
stream_list.highlight_first(all_streams, 'highlighted_user');
}
return user_info; // for testing
};
var update_users_for_search = _.throttle(exports.build_user_sidebar, 50);
function do_update_users_for_search() {
exports.build_user_sidebar();
exports.user_cursor.reset();
}
var update_users_for_search = _.throttle(do_update_users_for_search, 50);
function show_huddles() {
$('#group-pm-list').addClass("show");
@ -342,8 +335,7 @@ function focus_ping(want_redraw) {
if (want_redraw) {
presence.set_info(data.presences, data.server_timestamp);
exports.build_user_sidebar();
exports.update_huddles();
exports.redraw();
}
},
});
@ -371,7 +363,7 @@ exports.initialize = function () {
page_params.initial_servertime);
delete page_params.presences;
exports.set_user_list_filter();
exports.set_cursor_and_filter();
exports.build_user_sidebar();
exports.update_huddles();
@ -408,59 +400,72 @@ exports.set_user_status = function (email, info, server_time) {
exports.redraw = function () {
exports.build_user_sidebar();
exports.user_cursor.redraw();
exports.update_huddles();
};
exports.highlight_first_user = function () {
if ($('#user_presences li.user_sidebar_entry.narrow-filter.highlighted_user').length === 0) {
// Highlight
var all_streams = $('#user_presences li.user_sidebar_entry.narrow-filter');
stream_list.highlight_first(all_streams, 'highlighted_user');
}
exports.reset_users = function () {
// Call this when we're leaving the search widget.
exports.build_user_sidebar();
exports.user_cursor.clear();
};
exports.narrow_for_user = function (opts) {
var user_id = buddy_list.get_key_from_li({li: opts.li});
var email = people.get_person_from_user_id(user_id).email;
return exports.narrow_for_user_id({user_id: user_id});
};
exports.narrow_for_user_id = function (opts) {
var person = people.get_person_from_user_id(opts.user_id);
var email = person.email;
narrow.by('pm-with', email, {trigger: 'sidebar'});
exports.user_filter.clear_and_hide_search();
};
function keydown_enter_key() {
// Is there at least one user?
if ($('#user_presences li.user_sidebar_entry.narrow-filter').length > 0) {
// There must be a 'highlighted_user' user
var li = $('#user_presences li.user_sidebar_entry.narrow-filter.highlighted_user');
exports.narrow_for_user({li: li});
popovers.hide_all();
var user_id = exports.user_cursor.get_key();
if (user_id === undefined) {
return;
}
exports.narrow_for_user_id({user_id: user_id});
popovers.hide_all();
}
function keydown_user_filter(e) {
stream_list.keydown_filter(e, '#user_presences li.user_sidebar_entry.narrow-filter',
$('#user_presences'), 'highlighted_user', keydown_enter_key);
}
function focusout_user_filter() {
// Undo highlighting
$('#user_presences li.user_sidebar_entry.highlighted_user').removeClass('highlighted_user');
}
exports.set_user_list_filter = function () {
exports.user_filter = user_search({
update_list: update_users_for_search,
reset_items: exports.clear_highlight,
initialize_list_for_search: exports.highlight_first_user,
exports.set_cursor_and_filter = function () {
exports.user_cursor = list_cursor({
list: buddy_list,
highlight_class: 'highlighted_user',
});
exports.set_user_list_filter_handlers();
};
exports.user_filter = user_search({
update_list: update_users_for_search,
reset_items: exports.reset_users,
on_focus: exports.user_cursor.reset,
});
exports.set_user_list_filter_handlers = function () {
exports.user_filter.input_field()
.on('keydown', keydown_user_filter)
.on('blur', focusout_user_filter);
var $input = exports.user_filter.input_field();
$input.on('blur', exports.user_cursor.clear);
keydown_util.handle({
elem: $input,
handlers: {
enter_key: function () {
keydown_enter_key();
return true;
},
up_arrow: function () {
exports.user_cursor.prev();
return true;
},
down_arrow: function () {
exports.user_cursor.next();
return true;
},
},
});
};
exports.initiate_search = function () {

View File

@ -35,6 +35,36 @@ var buddy_list = (function () {
self.container.html(html);
};
self.first_key = function () {
var list_items = self.container.find(self.item_sel);
var li = list_items.first();
if (li.length === 0) {
return;
}
var key = self.get_key_from_li({li: li});
return key;
};
self.prev_key = function (key) {
var li = self.find_li({key: key});
var prev_li = li.prev();
if (prev_li.length === 0) {
return;
}
var prev_key = self.get_key_from_li({li: prev_li});
return prev_key;
};
self.next_key = function (key) {
var li = self.find_li({key: key});
var next_li = li.next();
if (next_li.length === 0) {
return;
}
var next_key = self.get_key_from_li({li: next_li});
return next_key;
};
self.maybe_remove_key = function (opts) {
var li = self.find_li({key: opts.key});
li.remove();

View File

@ -7,6 +7,7 @@ var exports = {};
*/
var keys = {
13: 'enter_key',
37: 'left_arrow',
38: 'up_arrow',
39: 'right_arrow',

142
static/js/list_cursor.js Normal file
View File

@ -0,0 +1,142 @@
var list_cursor = function (opts) {
var self = {};
var config_ok = (
opts.highlight_class &&
opts.list &&
opts.list.container &&
opts.list.find_li &&
opts.list.first_key &&
opts.list.prev_key &&
opts.list.next_key
);
if (!config_ok) {
blueslip.error('Programming error');
return;
}
self.clear = function () {
if (self.curr_key === undefined) {
return;
}
var row = self.get_row(self.curr_key);
if (row) {
row.clear();
}
self.curr_key = undefined;
};
self.get_key = function () {
return self.curr_key;
};
self.get_row = function (key) {
// TODO: The list class should probably do more of the work
// here, so we're not so coupled to jQuery, and
// so we instead just get back a widget we can say
// something like widget.select() on. This will
// be especially important if we do lazy rendering.
// It would also give the caller more flexibility on
// the actual styling.
if (key === undefined) {
return;
}
var li = opts.list.find_li({key: key});
if (li.length === 0) {
return;
}
return {
highlight: function () {
li.addClass(opts.highlight_class);
self.adjust_scroll(li);
},
clear: function () {
li.removeClass(opts.highlight_class);
},
};
};
self.adjust_scroll = function (li) {
// TODO: move scroll_element_into_container out of
// stream_list.js
stream_list.scroll_element_into_container(li, opts.list.container);
};
self.redraw = function () {
// We should only call this for situations like the buddy
// list where we redraw the whole list without necessarily
// changing it, so we just want to re-highlight the current
// row in the new DOM. If you are filtering, for now you
// should call the 'reset()' method.
var row = self.get_row(self.curr_key);
if (row === undefined) {
return;
}
row.highlight();
};
self.go_to = function (key) {
if (key === self.curr_key) {
return;
}
if (key === undefined) {
blueslip.error('Caller is not checking keys for list_cursor.go_to');
return;
}
self.clear();
self.curr_key = key;
var row = self.get_row(key);
if (row === undefined) {
blueslip.error('Cannot highlight key for list_cursor: ' + key);
return;
}
row.highlight();
};
self.reset = function () {
self.clear();
var key = opts.list.first_key();
if (key === undefined) {
self.curr_key = undefined;
return;
}
self.go_to(key);
};
self.prev = function () {
if (self.curr_key === undefined) {
return;
}
var key = opts.list.prev_key(self.curr_key);
if (key === undefined) {
// leave the current key
return;
}
self.go_to(key);
};
self.next = function () {
if (self.curr_key === undefined) {
// This is sort of a special case where we went from
// an empty filter to having data.
self.reset();
return;
}
var key = opts.list.next_key(self.curr_key);
if (key === undefined) {
// leave the current key
return;
}
self.go_to(key);
};
return self;
};
if (typeof module !== 'undefined') {
module.exports = list_cursor;
}

View File

@ -306,7 +306,7 @@ exports.update_streams_sidebar = function () {
$('#stream_filters li.highlighted_stream').removeClass('highlighted_stream');
if (exports.searching()) {
var all_streams = $('#stream_filters li.narrow-filter');
exports.highlight_first(all_streams, 'highlighted_stream');
exports.highlight_first(all_streams);
}
if (! narrow_state.active()) {
@ -452,7 +452,7 @@ function focus_stream_filter(e) {
if ($('#stream_filters li.narrow-filter.highlighted_stream').length === 0) {
// Highlight
var all_streams = $('#stream_filters li.narrow-filter');
exports.highlight_first(all_streams, 'highlighted_stream');
exports.highlight_first(all_streams);
}
e.stopPropagation();
}
@ -480,7 +480,7 @@ function keydown_enter_key() {
function keydown_stream_filter(e) {
exports.keydown_filter(e, '#stream_filters li.narrow-filter',
$('#stream-filters-container'), 'highlighted_stream', keydown_enter_key);
$('#stream-filters-container'), keydown_enter_key);
}
function actually_update_streams_for_search() {
@ -575,7 +575,7 @@ exports.initiate_search = function () {
// Highlight first result
var all_streams = $('#stream_filters li.narrow-filter');
exports.highlight_first(all_streams, 'highlighted_stream');
exports.highlight_first(all_streams);
};
exports.clear_and_hide_search = function () {
@ -595,16 +595,13 @@ function next_sibing_in_dir(elm, dir_up) {
return elm.next();
}
function keydown_arrow_key(dir_up, all_streams_selector,
scroll_container, highlighting_class) {
function keydown_arrow_key(dir_up, all_streams_selector, scroll_container) {
// Are there streams to cyle through?
if ($(all_streams_selector).length > 0) {
var current_sel = $(all_streams_selector + '.' + highlighting_class).expectOne();
var current_sel = $(all_streams_selector + '.highlighted_stream').expectOne();
var next_sibling = next_sibing_in_dir(current_sel, dir_up);
if (highlighting_class === 'highlighted_stream'
&& next_sibling.is('hr.stream-split')) {
// Only for the left sidebar
if (next_sibling.is('hr.stream-split')) {
// Skip separator
next_sibling = next_sibing_in_dir(next_sibling, dir_up);
}
@ -622,20 +619,15 @@ function keydown_arrow_key(dir_up, all_streams_selector,
}
// Classes must be explicitly named
if (highlighting_class === 'highlighted_stream') {
current_sel.removeClass('highlighted_stream');
next_sibling.addClass('highlighted_stream');
} else if (highlighting_class === 'highlighted_user') {
current_sel.removeClass('highlighted_user');
next_sibling.addClass('highlighted_user');
}
current_sel.removeClass('highlighted_stream');
next_sibling.addClass('highlighted_stream');
exports.scroll_element_into_container(next_sibling, scroll_container);
}
}
exports.keydown_filter = function (e, all_streams_selector, scroll_container,
highlighting_class, enter_press_function) {
enter_press_function) {
// Function for left and right sidebar
// Could be placed somewhere else but ui.js is already very full
@ -651,15 +643,13 @@ exports.keydown_filter = function (e, all_streams_selector, scroll_container,
}
case 38: {
// Up-arrow key was pressed
keydown_arrow_key(true, all_streams_selector,
scroll_container, highlighting_class);
keydown_arrow_key(true, all_streams_selector, scroll_container);
handled = true;
break;
}
case 40: {
// Down-arrow key was pressed
keydown_arrow_key(false, all_streams_selector,
scroll_container, highlighting_class);
keydown_arrow_key(false, all_streams_selector, scroll_container);
handled = true;
break;
}
@ -675,14 +665,10 @@ exports.keydown_filter = function (e, all_streams_selector, scroll_container,
}
};
exports.highlight_first = function (all_streams, highlighting_class) {
exports.highlight_first = function (all_streams) {
if (all_streams.length > 0) {
// Classes must be explicitly named
if (highlighting_class === 'highlighted_stream') {
all_streams.first().addClass('highlighted_stream');
} else if (highlighting_class === 'highlighted_user') {
all_streams.first().addClass('highlighted_user');
}
all_streams.first().addClass('highlighted_stream');
}
};

View File

@ -32,7 +32,7 @@ var user_search = function (opts) {
$input.val('');
$input.blur();
opts.update_list();
opts.reset_items();
};
self.escape_search = function () {
@ -87,7 +87,6 @@ var user_search = function (opts) {
self.expand_column();
self.show_widget();
$input.focus();
opts.initialize_list_for_search();
};
self.toggle_filter_displayed = function () {
@ -99,7 +98,7 @@ var user_search = function (opts) {
};
function on_focus(e) {
opts.initialize_list_for_search();
opts.on_focus();
e.stopPropagation();
}
@ -107,7 +106,7 @@ var user_search = function (opts) {
$('#userlist-header').on('click', self.toggle_filter_displayed);
$input.on('input', opts.update_list);
$input.on('click', on_focus);
$input.on('focus', on_focus);
return self;
};

View File

@ -1138,6 +1138,7 @@ JS_SPECS = {
'js/user_search.js',
'js/buddy_data.js',
'js/buddy_list.js',
'js/list_cursor.js',
'js/activity.js',
'js/user_events.js',
'js/colorspace.js',