Skip to content
This repository has been archived by the owner on Feb 12, 2022. It is now read-only.

Commit

Permalink
Merge pull request #1725 from cormacmccarthy/GH1710---expose-onkeyup-…
Browse files Browse the repository at this point in the history
…in-combobox-for-typeahead-supportfuelux

adds filter option to combobox and keyboard nav for dropdown
  • Loading branch information
Kevin Parkerson committed Feb 23, 2016
2 parents ddd60f3 + 16a6399 commit eb062a2
Show file tree
Hide file tree
Showing 7 changed files with 240 additions and 5 deletions.
1 change: 1 addition & 0 deletions bower.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
"requirejs-text": "2.x",
"underscore": "1.x",
"blanket": "1.x",
"jquery-simulate-ext": "~1.3.0",
"require-handlebars-plugin": "~1.0.0"
},
"ignore": [
Expand Down
5 changes: 4 additions & 1 deletion index.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,10 @@ define(function (require) {
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
COMBOBOX
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

$('#myCombobox').combobox({
filterOnKeypress: false,
showOptionsOnKeypress: true
});
// sample method buttons
$('#btnComboboxGetSelectedItem').on('click', function () {
var selectedItem = $('#myCombobox').combobox('selectedItem');
Expand Down
117 changes: 114 additions & 3 deletions js/combobox.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,12 @@
this.$dropMenu = this.$element.find('.dropdown-menu');
this.$input = this.$element.find('input');
this.$button = this.$element.find('.btn');
this.$inputGroupBtn = this.$element.find('.input-group-btn');

this.$element.on('click.fu.combobox', 'a', $.proxy(this.itemclicked, this));
this.$element.on('change.fu.combobox', 'input', $.proxy(this.inputchanged, this));
this.$element.on('shown.bs.dropdown', $.proxy(this.menuShown, this));
this.$input.on('keyup.fu.combobox', $.proxy(this.keypress, this));

// set default selection
this.setDefaultSelection();
Expand All @@ -53,6 +55,11 @@
this.$button.addClass('disabled');
}

// filter on load in case the first thing they do is press navigational key to pop open the menu
if (this.options.filterOnKeypress) {
this.options.filter(this.$dropMenu.find('li'), this.$input.val(), this);
}

};

Combobox.prototype = {
Expand All @@ -77,13 +84,20 @@

doSelect: function ($item) {
if (typeof $item[0] !== 'undefined') {
$item.addClass('selected');
this.$selectedItem = $item;
this.$input.val(this.$selectedItem.text().trim());
} else {
this.$selectedItem = null;
}
},

clearSelection: function () {
this.$selectedItem = null;
this.$input.val('');
this.$dropMenu.find('li').removeClass('selected');
},

menuShown: function () {
if (this.options.autoResizeMenu) {
this.resizeMenu();
Expand All @@ -106,7 +120,7 @@
}, this.$selectedItem.data());
} else {
data = {
text: this.$input.val()
text: this.$input.val().trim()
};
}

Expand All @@ -116,11 +130,12 @@
selectByText: function (text) {
var $item = $([]);
this.$element.find('li').each(function () {
if ((this.textContent || this.innerText || $(this).text() || '').toLowerCase() === (text || '').toLowerCase()) {
if ((this.textContent || this.innerText || $(this).text() || '').trim().toLowerCase() === (text || '').trim().toLowerCase()) {
$item = $(this);
return false;
}
});

this.doSelect($item);
},

Expand Down Expand Up @@ -185,6 +200,75 @@
this.$element.find('.dropdown-toggle').focus();
},

keypress: function (e) {
var ENTER = 13;
//var TAB = 9;
var ESC = 27;
var LEFT = 37;
var UP = 38;
var RIGHT = 39;
var DOWN = 40;

var IS_NAVIGATIONAL = (
e.which === UP ||
e.which === DOWN ||
e.which === LEFT ||
e.which === RIGHT
);

if(this.options.showOptionsOnKeypress && !this.$inputGroupBtn.hasClass('open')){
this.$button.dropdown('toggle');
this.$input.focus();
}

if (e.which === ENTER) {
e.preventDefault();

var selected = this.$dropMenu.find('li.selected').text().trim();
if(selected.length > 0){
this.selectByText(selected);
}else{
this.selectByText(this.$input.val());
}

this.$inputGroupBtn.removeClass('open');
this.inputchanged(e);
} else if (e.which === ESC) {
e.preventDefault();
this.clearSelection();
this.$inputGroupBtn.removeClass('open');
} else if (this.options.showOptionsOnKeypress) {
if (e.which === DOWN || e.which === UP) {
e.preventDefault();
var $selected = this.$dropMenu.find('li.selected');
if ($selected.length > 0) {
if (e.which === DOWN) {
$selected = $selected.next(':not(.hidden)');
} else {
$selected = $selected.prev(':not(.hidden)');
}
}

if ($selected.length === 0){
if (e.which === DOWN) {
$selected = this.$dropMenu.find('li:not(.hidden):first');
} else {
$selected = this.$dropMenu.find('li:not(.hidden):last');
}
}
this.$dropMenu.find('li').removeClass('selected');
$selected.addClass('selected');
}
}

// Avoid filtering on navigation key presses
if (this.options.filterOnKeypress && !IS_NAVIGATIONAL) {
this.options.filter(this.$dropMenu.find('li'), this.$input.val(), this);
}

this.previousKeyPress = e.which;
},

inputchanged: function (e, extra) {
// skip processing for internally-generated synthetic event
// to avoid double processing
Expand Down Expand Up @@ -232,7 +316,34 @@
};

$.fn.combobox.defaults = {
autoResizeMenu: true
autoResizeMenu: true,
filterOnKeypress: false,
showOptionsOnKeypress: false,
filter: function filter (list, predicate, self) {
var visible = 0;
self.$dropMenu.find('.empty-indicator').remove();

list.each(function (i) {
var $li = $(this);
var text = $(this).text().trim();

$li.removeClass();

if (text === predicate) {
$li.addClass('text-success');
visible++;
} else if (text.substr(0, predicate.length) === predicate) {
$li.addClass('text-info');
visible++;
} else {
$li.addClass('hidden');
}
});

if (visible === 0) {
self.$dropMenu.append('<li class="empty-indicator text-muted"><em>No Matches</em></li>');
}
}
};

$.fn.combobox.Constructor = Combobox;
Expand Down
17 changes: 17 additions & 0 deletions less/combobox.less
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,23 @@
display:none;
}
}
.dropdown-menu > li.selected > a {
color: #262626;
text-decoration: none;
background-color: #f5f5f5;
}

.dropdown-menu > li > em {
display: block;
padding: 3px 20px;
clear: both;
font-weight: normal;
line-height: 1.42857143;
color: #333;
white-space: nowrap;
}
}



}
52 changes: 52 additions & 0 deletions test/combobox-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@

define(function(require){
var $ = require('jquery');
require('sim/libs/bililiteRange');
require('sim/libs/jquery.simulate');
require('sim/src/jquery.simulate.ext');
require('sim/src/jquery.simulate.drag-n-drop');
require('sim/src/jquery.simulate.key-sequence');
require('sim/src/jquery.simulate.key-combo');
var html = require('text!test/markup/combobox-markup.html!strip');
/* FOR DEV TESTING - uncomment to test against index.html */
//html = require('text!index.html!strip');
Expand Down Expand Up @@ -110,6 +116,52 @@ define(function(require){
deepEqual(item, expectedItem, 'item selected');
});

var userInteracts = function userInteracts($combobox) {
var DOWN_KEY = 40;
var UP_KEY = 38;
var deleteAllTypeT = "{backspace}{backspace}{backspace}{backspace}{backspace}T";
var hitEnter = "{enter}";

var hitDown = jQuery.Event("keyup");
hitDown.which = DOWN_KEY;
hitDown.keyCode = DOWN_KEY;
hitDown.keypress = DOWN_KEY;

$combobox.find('input').simulate("key-sequence", {sequence: deleteAllTypeT});
$combobox.find('input').trigger(hitDown);
$combobox.find('input').simulate("key-sequence", {sequence: hitEnter});
};

test("should respond to keypresses appropriately with filter and showOptionsOnKeypress off", function() {
var $combobox = $(html).find("#MyCombobox").combobox();

userInteracts($combobox);

var item = $combobox.combobox('selectedItem');
var expectedItem = { text:'T' };
deepEqual(item, expectedItem, 'Combobox was not triggered, filter not activated');
});

test("should respond to keypresses appropriately with filter off and showOptionsOnKeypress on", function() {
var $combobox = $(html).find("#MyComboboxWithSelectedForOptions").combobox({ showOptionsOnKeypress: true });

userInteracts($combobox);

var item = $combobox.combobox('selectedItem');
var expectedItem = { text:'Four', value: 4 };
deepEqual(item, expectedItem, 'Combobox was triggered with filter inactive but showOptionsOnKeypress active');
});

test("should respond to keypresses appropriately with filter and showOptionsOnKeypress on", function() {
var $combobox = $(html).find("#MyComboboxWithSelectedForFilter").combobox({ showOptionsOnKeypress: true, filterOnKeypress: true });

userInteracts($combobox);

var item = $combobox.combobox('selectedItem');
var expectedItem = { text:'Two', value: 2 };
deepEqual(item, expectedItem, 'Combobox was triggered with filter active');
});

test("should select by text with whitespace", function() {
var $combobox = $(html).find("#MyCombobox").combobox();
$combobox.combobox('selectByText', 'Item Five');
Expand Down
50 changes: 50 additions & 0 deletions test/markup/combobox-markup.html
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,56 @@
</div>
</div>

<div class="form-group">
<label class="col-sm-2 control-label" for="MyComboboxWithSelectedForFilterInput">Has selected item</label>
<div class="col-sm-10">

<div id="MyComboboxWithSelectedForFilter" class="input-group input-append dropdown combobox">
<input id="MyComboboxWithSelectedForFilterInput" type="text" class="form-control">
<div class="input-group-btn">
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown">
<span class="caret"></span>
<span class="sr-only">Toggle Dropdown</span>
</button>
<ul class="dropdown-menu dropdown-menu-right" aria-hidden="true" role="menu">
<li data-value="1"><a href="#">One</a></li>
<li data-value="2"><a href="#">Two</a></li>
<li data-value="3" data-selected="true"><a href="#">Three</a></li>
<li data-value="4" ><a href="#">Four</a></li>
<li data-value="Item Five"><a href="#">Item Five</a></li>
<li data-value="6" data-foo="bar" data-fizz="buzz"><a href="#">Six</a></li>
</ul>
</div>
</div>

</div>
</div>

<div class="form-group">
<label class="col-sm-2 control-label" for="MyComboboxWithSelectedForOptionsInput">Has selected item</label>
<div class="col-sm-10">

<div id="MyComboboxWithSelectedForOptions" class="input-group input-append dropdown combobox">
<input id="MyComboboxWithSelectedForOptionsInput" type="text" class="form-control">
<div class="input-group-btn">
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown">
<span class="caret"></span>
<span class="sr-only">Toggle Dropdown</span>
</button>
<ul class="dropdown-menu dropdown-menu-right" aria-hidden="true" role="menu">
<li data-value="1"><a href="#">One</a></li>
<li data-value="2"><a href="#">Two</a></li>
<li data-value="3" data-selected="true"><a href="#">Three</a></li>
<li data-value="4" ><a href="#">Four</a></li>
<li data-value="Item Five"><a href="#">Item Five</a></li>
<li data-value="6" data-foo="bar" data-fizz="buzz"><a href="#">Six</a></li>
</ul>
</div>
</div>

</div>
</div>

<div id="MyComboboxSingleItem" class="input-group input-append dropdown combobox">
<input id="MyComboBoxInput1" type="text" class="form-control">
<div class="input-group-btn">
Expand Down
3 changes: 2 additions & 1 deletion test/tests.html
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,8 @@
bootstrap: 'bower_components/bootstrap/dist/js/bootstrap',
fuelux: 'js',
tests: window.noMoment ? 'test/tests-no-moment' : 'test/tests' ,
text: 'bower_components/requirejs-text/text'
text: 'bower_components/requirejs-text/text',
'sim': 'bower_components/jquery-simulate-ext'
},
shim: {
'bootstrap': {
Expand Down

0 comments on commit eb062a2

Please sign in to comment.