-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Thomas Rix
committed
Feb 28, 2012
1 parent
c6a6687
commit 72a0400
Showing
4 changed files
with
324 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
.ui-bubblebox-list { | ||
list-style: none; | ||
padding: 0; | ||
margin: 10px 0; | ||
} | ||
|
||
.ui-bubblebox-item { | ||
border:1px solid #cdd5e6; | ||
background:#eff2f7; | ||
color:#6a6a6a; | ||
border-radius: 3px; | ||
-moz-border-radius: 3px; | ||
|
||
padding: 5px 15px 5px 5px; | ||
margin: 5px 0; | ||
|
||
position: relative; | ||
} | ||
|
||
.ui-bubblebox-item span { | ||
display: block; | ||
white-space: nowrap; | ||
overflow: hidden; | ||
text-overflow: ellipsis; | ||
} | ||
|
||
.ui-bubblebox-removeItem { | ||
font-size:12px; | ||
font-weight:bold; | ||
cursor:pointer; | ||
color:#abb8d4; | ||
|
||
position: absolute; | ||
right: 5px; | ||
top: 5px; | ||
} | ||
.ui-bubblebox-removeItem:hover { | ||
color: #bbb; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
/*! bubbleBox: a jQuery UI widget to manage a list of items | ||
http://github.com/rixth/bubbleBox | ||
*/ | ||
|
||
/*jshint browser: true, jquery: true, indent: 2, white: true, curly: true, forin: true, noarg: true, immed: true, newcap: true, noempty: true */ | ||
(function ($) { | ||
$.widget("ui.bubbleBox", { | ||
options: { | ||
showRemoveButton: true, | ||
list: null, | ||
allowDupes: false, | ||
seedData: [], | ||
triggerKeyCodes: [ | ||
13, // enter | ||
44, 188, // the two possibilities for comma | ||
59, 186 // the two possibilities for semicolon | ||
] | ||
}, | ||
_create: function () { | ||
var self = this; | ||
|
||
self.input = self.element; | ||
|
||
self.options.list || (self.options.list = '#' + self.input.attr('id') + '_list'); | ||
self.list = $(self.options.list).addClass('ui-bubblebox-list'); | ||
|
||
// Bind events | ||
if (self.options.showRemoveButton) { | ||
self.list.delegate('.ui-bubblebox-removeItem', 'click', function (event) { | ||
self.removeItem($(this.parentNode)); | ||
}); | ||
} | ||
|
||
self.element.bind('blur keydown', function (event) { | ||
var newValue = self.input.val(); | ||
|
||
if (event.type === 'blur' || jQuery.inArray(event.which, self.options.triggerKeyCodes) !== -1) { | ||
if (self.addItem(newValue, event)) { | ||
self.input.val(''); | ||
event.preventDefault(); | ||
} | ||
} else if (newValue === '' && event.type === 'keydown' && event.which === 8) { | ||
self.removeItem(self.list.find('li:last-child')); | ||
} | ||
}); | ||
|
||
// Insert start data | ||
$(self.options.seedData).each(function () { | ||
self.addItem(this); | ||
}); | ||
}, | ||
_createBubble: function (value) { | ||
var removeButton = this.options.showRemoveButton ? '<div class="ui-bubblebox-removeItem">x</div>' : ''; | ||
return $('<li class="ui-bubblebox-item"><span>' + value + '</span>' + removeButton + '</li>'); | ||
}, | ||
val: function () { | ||
var values = []; | ||
this.list.children().each(function () { | ||
values.push($(this).find('span').text()); | ||
}); | ||
return values; | ||
}, | ||
removeItem: function (item, event) { | ||
item.remove(); | ||
this._trigger('remove', event, { value: item.find('span').text(), node: item[0] }); | ||
}, | ||
addItem: function (value, event) { | ||
var self = this, | ||
bubble; | ||
|
||
if (!self.options.allowDupes && jQuery.inArray(value, self.val()) !== -1) { | ||
return false; | ||
} | ||
|
||
if (value !== '' && !value.match(/^\s+$/) && self._trigger('beforeAdd', event, { value: value }) !== false) { | ||
bubble = self._createBubble(value); | ||
self.list.append(bubble); | ||
self._trigger('afterAdd', event, { value: value, node: bubble[0] }); | ||
return bubble; | ||
} else { | ||
return false; | ||
} | ||
}, | ||
_destroy: function () { | ||
this.list.empty().removeClass('ui-bubblebox-list'); | ||
$.Widget.prototype.destroy.apply(this, arguments); | ||
} | ||
}); | ||
}(jQuery)); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,194 @@ | ||
/*globals jasmine, describe, it, beforeEach, setFixtures, expect */ | ||
|
||
describe("bubblebox", function () { | ||
function resetHtml() { | ||
setFixtures('<ul id="bubbleBox_list"></ul><div id="bubbleBox"></div>'); | ||
} | ||
|
||
var itemSelector = '.ui-bubblebox-item', | ||
box, list; | ||
|
||
beforeEach(function() { | ||
resetHtml(); | ||
$('#bubbleBox').bubbleBox(); | ||
box = $('#bubbleBox'); | ||
list = $('#bubbleBox_list'); | ||
}); | ||
|
||
function addThreeItems() { | ||
box.bubbleBox("addItem", "my value 1"); | ||
box.bubbleBox("addItem", "my value 2"); | ||
box.bubbleBox("addItem", "my value 3"); | ||
} | ||
|
||
function numberOfItems() { | ||
return $('#bubbleBox_list ' + itemSelector).length; | ||
} | ||
|
||
describe("adding items", function () { | ||
var enterEvent = $.Event('keydown'); | ||
enterEvent.which = $.ui.keyCode.ENTER; | ||
|
||
it("should do nothing when the input is empty", function () { | ||
box.trigger(enterEvent); | ||
expect(numberOfItems()).toEqual(0); | ||
}); | ||
it("should add a new item when enter is pressed", function () { | ||
box.val('my value'); | ||
box.trigger(enterEvent); | ||
expect(numberOfItems()).toEqual(1); | ||
}); | ||
it("should clear the input after an item is added", function () { | ||
box.val('my value'); | ||
box.trigger(enterEvent); | ||
expect(numberOfItems()).toEqual(1); | ||
expect(box.val()).toEqual(''); | ||
}); | ||
it("should add a new item when comma is pressed", function () { | ||
box.val('my value'); | ||
var commaEvent = $.Event('keydown'); | ||
|
||
commaEvent.which = $.ui.keyCode.COMMA; | ||
box.trigger(commaEvent); | ||
expect(numberOfItems()).toEqual(1); | ||
}); | ||
it("should add a new item when semicolon is pressed", function () { | ||
box.val('my value'); | ||
var semicolonEvent = $.Event('keydown'); | ||
|
||
semicolonEvent.which = 59; | ||
box.trigger(semicolonEvent); | ||
expect(numberOfItems()).toEqual(1); | ||
}); | ||
it("should add a new item when input focus is lost", function () { | ||
box.val('my value'); | ||
box.trigger($.Event('blur')); | ||
expect($('#bubbleBox_list ' + itemSelector).length).toEqual(1); | ||
}); | ||
|
||
describe("duplicate item handling", function () { | ||
it("should not allow duplicates by default", function () { | ||
addThreeItems(); | ||
expect(numberOfItems()).toEqual(3); | ||
box.val('my value 1'); | ||
box.trigger(enterEvent); | ||
expect(numberOfItems()).toEqual(3); | ||
}); | ||
it("should allow duplicates when allowDupes is true", function () { | ||
box.bubbleBox("option", "allowDupes", true); | ||
addThreeItems(); | ||
expect(numberOfItems()).toEqual(3); | ||
box.val('my value 1'); | ||
box.trigger(enterEvent); | ||
expect(numberOfItems()).toEqual(4); | ||
}); | ||
}); | ||
|
||
describe("seeding start data", function () { | ||
it("should add items upon instantiation", function () { | ||
resetHtml(); | ||
box.bubbleBox({ | ||
seedData: ['Trulia', 'Zillow'] | ||
}); | ||
expect(numberOfItems()).toEqual(2); | ||
expect(box.bubbleBox("val").join(",")).toEqual("Trulia,Zillow"); | ||
}); | ||
}); | ||
}); | ||
|
||
describe("removing items", function () { | ||
it("should remove items when the close button is clicked", function () { | ||
addThreeItems(); | ||
expect(numberOfItems()).toEqual(3); | ||
$('#bubbleBox_list>li>div').eq(0).click(); | ||
expect(numberOfItems()).toEqual(2); | ||
}); | ||
it("should remove the last item when the delete key is pressed when the input is blank", function () { | ||
var backspaceEvent = $.Event('keydown'), | ||
box = $('#bubbleBox'); | ||
backspaceEvent.which = 8; | ||
|
||
addThreeItems(); | ||
box.trigger(backspaceEvent); | ||
expect(box.bubbleBox("val").join(',')).toEqual('my value 1,my value 2'); | ||
}); | ||
}); | ||
|
||
describe("events", function () { | ||
describe("add events", function () { | ||
var enterEvent = $.Event('keydown'); | ||
enterEvent.which = $.ui.keyCode.ENTER; | ||
|
||
it("should trigger the beforeAdd event before a new item is added", function () { | ||
box.val('my value'); | ||
var callback = jasmine.createSpy(); | ||
|
||
box.bind('bubbleboxbeforeadd', callback); | ||
box.trigger(enterEvent); | ||
expect(callback).toHaveBeenCalledWith(jasmine.any(Object), { value: 'my value' }); | ||
}); | ||
it("should not add an item if any of the beforeAdd handlers return false", function () { | ||
box.val('my value'); | ||
var callback = jasmine.createSpy().andReturn(false); | ||
|
||
box.bind('bubbleboxbeforeadd', callback); | ||
box.trigger(enterEvent); | ||
|
||
expect(callback).toHaveBeenCalledWith(jasmine.any(Object), { value: 'my value' }); | ||
expect(numberOfItems()).toEqual(0); | ||
}); | ||
it("should trigger the afterAdd event after a new item is added", function () { | ||
box.val('my value'); | ||
var callback = jasmine.createSpy(); | ||
|
||
box.bind('bubbleboxafteradd', callback); | ||
box.trigger(enterEvent); | ||
|
||
expect(callback).toHaveBeenCalledWith(jasmine.any(Object), { value: 'my value', node: jasmine.any(Object)}); | ||
expect(numberOfItems()).toEqual(1); | ||
}); | ||
}); | ||
|
||
describe("remove events", function () { | ||
it("should trigger the remove event when an event is removed via the delete key", function () { | ||
var backspaceEvent = $.Event('keydown'), | ||
callback = jasmine.createSpy(); | ||
|
||
backspaceEvent.which = 8; | ||
|
||
box.bind('bubbleboxremove', callback); | ||
addThreeItems(); | ||
box.trigger(backspaceEvent); | ||
expect(numberOfItems()).toEqual(2); | ||
expect(callback).toHaveBeenCalledWith(jasmine.any(Object), { value: 'my value 3', node: jasmine.any(Object)}); | ||
}); | ||
it("should trigger the remove event when an event is removed via the remove button", function () { | ||
var callback = jasmine.createSpy(); | ||
box.bind('bubbleboxremove', callback); | ||
addThreeItems(); | ||
$('#bubbleBox_list>li>div').eq(0).click(); | ||
expect(numberOfItems()).toEqual(2); | ||
expect(callback).toHaveBeenCalledWith(jasmine.any(Object), { value: 'my value 1', node: jasmine.any(Object)}); | ||
}); | ||
}); | ||
}); | ||
|
||
describe("fetching the value", function () { | ||
it("should return an array when the val() method is called", function () { | ||
addThreeItems(); | ||
expect(box.bubbleBox("val").length).toEqual(3); | ||
expect(box.bubbleBox("val")).toContain('my value 1'); | ||
expect(box.bubbleBox("val")).toContain('my value 2'); | ||
expect(box.bubbleBox("val")).toContain('my value 3'); | ||
}); | ||
}); | ||
|
||
describe("destroying", function () { | ||
it("should empty the container upon destruction", function () { | ||
addThreeItems(); | ||
expect(numberOfItems()).toEqual(3); | ||
box.bubbleBox("destroy"); | ||
expect(numberOfItems()).toEqual(3); | ||
}); | ||
}); | ||
}); |