All checks were successful
Publish To Prod / deploy_and_publish (push) Successful in 35s
535 lines
15 KiB
JavaScript
535 lines
15 KiB
JavaScript
var assert = require('power-assert');
|
|
var simulant = require('simulant');
|
|
var queue = require('d3-queue').queue;
|
|
var createFocusGroup = require('..');
|
|
|
|
var arrowUpEvent = { keyCode: 38 };
|
|
var arrowDownEvent = { keyCode: 40 };
|
|
var arrowLeftEvent = { keyCode: 37 };
|
|
var arrowRightEvent = { keyCode: 39 };
|
|
|
|
var nodeOne = document.createElement('button');
|
|
nodeOne.innerHTML = 'one';
|
|
document.body.appendChild(nodeOne);
|
|
var nodeTwo = document.createElement('button');
|
|
nodeTwo.innerHTML = 'two';
|
|
document.body.appendChild(nodeTwo);
|
|
var nodeThree = document.createElement('button');
|
|
nodeThree.innerHTML = 'three';
|
|
document.body.appendChild(nodeThree);
|
|
// nodeFour has no text content
|
|
var nodeFour = document.createElement('button');
|
|
document.body.appendChild(nodeFour);
|
|
|
|
describe('default settings', function() {
|
|
beforeEach(function() {
|
|
this.focusGroup = createFocusGroup({
|
|
members: [nodeOne, nodeTwo, nodeThree],
|
|
}).activate();
|
|
});
|
|
|
|
afterEach(function() {
|
|
this.focusGroup.deactivate();
|
|
});
|
|
|
|
it('with focus outside the group, arrow and letter keys do not affect focus', function() {
|
|
nodeFour.focus(); // nodeFour is outside group
|
|
simulateKeydown(arrowUpEvent);
|
|
assertActiveElement(nodeFour);
|
|
simulateKeydown(arrowDownEvent);
|
|
assertActiveElement(nodeFour);
|
|
simulateKeydown(arrowLeftEvent);
|
|
assertActiveElement(nodeFour);
|
|
simulateKeydown(arrowRightEvent);
|
|
assertActiveElement(nodeFour);
|
|
simulateKeydown({ keyCode: 70 });
|
|
assertActiveElement(nodeFour);
|
|
simulateKeydown({ keyCode: 88 });
|
|
assertActiveElement(nodeFour);
|
|
});
|
|
|
|
describe('with focus inside the group', function() {
|
|
it('down arrow moves focus to next node and sticks on last', function() {
|
|
nodeOne.focus();
|
|
simulateKeydown(arrowDownEvent);
|
|
assertActiveElement(nodeTwo);
|
|
simulateKeydown(arrowDownEvent);
|
|
assertActiveElement(nodeThree);
|
|
simulateKeydown(arrowDownEvent);
|
|
assertActiveElement(nodeThree);
|
|
});
|
|
|
|
it('up arrow moves focus to previous node and sticks on first', function() {
|
|
nodeThree.focus();
|
|
simulateKeydown(arrowUpEvent);
|
|
assertActiveElement(nodeTwo);
|
|
simulateKeydown(arrowUpEvent);
|
|
assertActiveElement(nodeOne);
|
|
simulateKeydown(arrowUpEvent);
|
|
assertActiveElement(nodeOne);
|
|
});
|
|
|
|
it('left and right arrows and letters do not move focus', function() {
|
|
nodeTwo.focus();
|
|
simulateKeydown(arrowLeftEvent);
|
|
assertActiveElement(nodeTwo);
|
|
simulateKeydown(arrowRightEvent);
|
|
assertActiveElement(nodeTwo);
|
|
simulateKeydown({ keyCode: 70 });
|
|
assertActiveElement(nodeTwo);
|
|
simulateKeydown({ keyCode: 88 });
|
|
assertActiveElement(nodeTwo);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('all arrows designated', function() {
|
|
beforeEach(function() {
|
|
this.focusGroup = createFocusGroup({
|
|
members: [nodeOne, nodeTwo, nodeThree],
|
|
keybindings: {
|
|
next: [arrowDownEvent, arrowRightEvent],
|
|
prev: [arrowUpEvent, arrowLeftEvent],
|
|
},
|
|
wrap: true
|
|
}).activate();
|
|
});
|
|
|
|
afterEach(function() {
|
|
this.focusGroup.deactivate();
|
|
});
|
|
|
|
it('up and left arrows move focus forward', function() {
|
|
nodeOne.focus();
|
|
simulateKeydown(arrowUpEvent);
|
|
assertActiveElement(nodeThree);
|
|
simulateKeydown(arrowLeftEvent);
|
|
assertActiveElement(nodeTwo);
|
|
});
|
|
|
|
it('down and right arrows move focus back', function() {
|
|
nodeThree.focus();
|
|
simulateKeydown(arrowDownEvent);
|
|
assertActiveElement(nodeOne);
|
|
simulateKeydown(arrowRightEvent);
|
|
assertActiveElement(nodeTwo);
|
|
});
|
|
});
|
|
|
|
describe('modifier keys', function() {
|
|
beforeEach(function () {
|
|
this.focusGroup = createFocusGroup({
|
|
members: [nodeOne, nodeTwo, nodeThree],
|
|
keybindings: {
|
|
first: { keyCode: 36, ctrlKey: true },
|
|
last: { keyCode: 35, ctrlKey: true },
|
|
},
|
|
}).activate();
|
|
});
|
|
|
|
afterEach(function () {
|
|
this.focusGroup.deactivate();
|
|
});
|
|
|
|
it('move focus to first node when holding control and hitting home key', function() {
|
|
nodeThree.focus();
|
|
simulateKeydown({ keyCode: 36, ctrlKey: true });
|
|
assertActiveElement(nodeOne);
|
|
})
|
|
|
|
it('move focus to last node when holding control and hitting end key', function() {
|
|
nodeOne.focus();
|
|
simulateKeydown({ keyCode: 35, ctrlKey: true });
|
|
assertActiveElement(nodeThree);
|
|
})
|
|
});
|
|
|
|
describe('all keybinding properties used', function() {
|
|
beforeEach(function () {
|
|
this.focusGroup = createFocusGroup({
|
|
members: [nodeOne, nodeTwo, nodeThree],
|
|
keybindings: {
|
|
next: arrowDownEvent,
|
|
prev: arrowUpEvent,
|
|
first: { keyCode: 36 },
|
|
last: { keyCode: 35 },
|
|
},
|
|
}).activate();
|
|
});
|
|
|
|
afterEach(function () {
|
|
this.focusGroup.deactivate();
|
|
});
|
|
|
|
it('move focus forward when hitting down arrow key', function() {
|
|
nodeOne.focus();
|
|
simulateKeydown(arrowDownEvent);
|
|
assertActiveElement(nodeTwo);
|
|
})
|
|
|
|
it('move focus backwards when hitting up arrow key', function() {
|
|
nodeTwo.focus();
|
|
simulateKeydown(arrowUpEvent);
|
|
assertActiveElement(nodeOne);
|
|
})
|
|
|
|
it('move focus to first node when hitting home key', function() {
|
|
nodeThree.focus();
|
|
simulateKeydown({ keyCode: 36 });
|
|
assertActiveElement(nodeOne);
|
|
})
|
|
|
|
it('move focus to last node when hitting end key', function() {
|
|
nodeOne.focus();
|
|
simulateKeydown({ keyCode: 35 });
|
|
assertActiveElement(nodeThree);
|
|
})
|
|
});
|
|
|
|
describe('arrows with modifier keys should not move focus by default', function() {
|
|
beforeEach(function() {
|
|
this.focusGroup = createFocusGroup({
|
|
members: [nodeOne, nodeTwo, nodeThree]
|
|
}).activate();
|
|
});
|
|
|
|
afterEach(function() {
|
|
this.focusGroup.deactivate();
|
|
});
|
|
|
|
it('down arrow with meta key should not move forward', function() {
|
|
nodeOne.focus();
|
|
simulateKeydown({ key: 'ArrowDown', metaKey: true });
|
|
assertActiveElement(nodeOne);
|
|
});
|
|
|
|
it('up arrow with shift key should not move back', function() {
|
|
nodeTwo.focus();
|
|
simulateKeydown({ key: 'ArrowUp', shiftKey: true });
|
|
assertActiveElement(nodeTwo);
|
|
});
|
|
});
|
|
|
|
describe('wrap: true', function() {
|
|
beforeEach(function() {
|
|
this.focusGroup = createFocusGroup({
|
|
members: [nodeOne, nodeTwo, nodeThree],
|
|
wrap: true,
|
|
}).activate();
|
|
});
|
|
|
|
afterEach(function() {
|
|
this.focusGroup.deactivate();
|
|
});
|
|
|
|
it('down arrow wraps forward', function() {
|
|
nodeTwo.focus();
|
|
simulateKeydown(arrowDownEvent);
|
|
assertActiveElement(nodeThree);
|
|
simulateKeydown(arrowDownEvent);
|
|
assertActiveElement(nodeOne);
|
|
});
|
|
|
|
it('up arrow wraps back', function() {
|
|
nodeTwo.focus();
|
|
simulateKeydown(arrowUpEvent);
|
|
assertActiveElement(nodeOne);
|
|
simulateKeydown(arrowUpEvent);
|
|
assertActiveElement(nodeThree);
|
|
});
|
|
});
|
|
|
|
describe('stringSearch: true', function(done) {
|
|
before(function() {
|
|
this.focusGroup = createFocusGroup({
|
|
members: [nodeOne, nodeTwo, nodeThree, nodeFour],
|
|
stringSearch: true,
|
|
}).activate();
|
|
});
|
|
|
|
after(function() {
|
|
this.focusGroup.deactivate();
|
|
});
|
|
|
|
var q = queue(1);
|
|
q.defer(function(next) {
|
|
nodeThree.focus();
|
|
it('moves on first keystroke', function() {
|
|
simulateKeydown({ keyCode: 79 }); // "o"
|
|
assertActiveElement(nodeOne);
|
|
});
|
|
next();
|
|
});
|
|
q.defer(function(next) {
|
|
setTimeout(function() {
|
|
it('300ms continues old search', function() {
|
|
simulateKeydown({ keyCode: 84 }); // "t"
|
|
assertActiveElement(nodeOne);
|
|
});
|
|
next();
|
|
}, 300);
|
|
});
|
|
q.defer(function(next) {
|
|
setTimeout(function() {
|
|
it('805ms starts new search', function() {
|
|
simulateKeydown({ keyCode: 84 }); // "t"
|
|
assertActiveElement(nodeTwo);
|
|
});
|
|
next();
|
|
}, 805);
|
|
});
|
|
q.defer(function(next) {
|
|
setTimeout(function() {
|
|
it('another key after 10ms extends search', function() {
|
|
simulateKeydown({ keyCode: 72 }); // "h"
|
|
assertActiveElement(nodeThree);
|
|
});
|
|
next();
|
|
}, 10);
|
|
});
|
|
q.defer(function(next) {
|
|
setTimeout(function() {
|
|
it('another key that produces an irrelevant search does not move focus', function() {
|
|
simulateKeydown({ keyCode: 79 }); // "o"
|
|
assertActiveElement(nodeThree);
|
|
});
|
|
next();
|
|
}, 10);
|
|
});
|
|
q.defer(function(next) {
|
|
setTimeout(function() {
|
|
it('after another 805ms the search has reset', function() {
|
|
simulateKeydown({ keyCode: 84 }); // "t"
|
|
assertActiveElement(nodeTwo);
|
|
});
|
|
next();
|
|
}, 805);
|
|
});
|
|
q.awaitAll(function(err) {
|
|
if (err) throw err;
|
|
done();
|
|
});
|
|
})
|
|
|
|
describe('stringSearch: true but no search happens', function() {
|
|
beforeEach(function() {
|
|
this.focusGroup = createFocusGroup({
|
|
members: [nodeOne, nodeTwo, nodeThree, nodeFour],
|
|
stringSearch: true,
|
|
}).activate();
|
|
});
|
|
|
|
afterEach(function() {
|
|
this.focusGroup.deactivate();
|
|
});
|
|
|
|
it('non-letters do nothing', function() {
|
|
nodeOne.focus();
|
|
simulateKeydown({ keyCode: 54 }); // "5"
|
|
assertActiveElement(nodeOne);
|
|
simulateKeydown({ keyCode: 188 }); // ","
|
|
assertActiveElement(nodeOne);
|
|
});
|
|
|
|
it('letters keys do nothing when ctrl, meta, or alt are also pressed', function() {
|
|
nodeOne.focus();
|
|
simulateKeydown({ keyCode: 84, ctrlKey: true }) // "t"
|
|
assertActiveElement(nodeOne);
|
|
simulateKeydown({ keyCode: 84, altKey: true }) // "t"
|
|
assertActiveElement(nodeOne);
|
|
simulateKeydown({ keyCode: 84, metaKey: true }) // "t"
|
|
assertActiveElement(nodeOne);
|
|
});
|
|
});
|
|
|
|
describe('deactivate()', function() {
|
|
beforeEach(function() {
|
|
this.focusGroup = createFocusGroup({ members: [nodeOne, nodeTwo, nodeThree] }).activate()
|
|
});
|
|
|
|
afterEach(function() {
|
|
this.focusGroup.deactivate();
|
|
});
|
|
|
|
it('does not respond after deactivation', function() {
|
|
this.focusGroup.deactivate()
|
|
nodeOne.focus();
|
|
simulateKeydown(arrowDownEvent);
|
|
assertActiveElement(nodeOne);
|
|
simulateKeydown({ keyCode: 84 }); // "t"
|
|
assertActiveElement(nodeOne);
|
|
});
|
|
});
|
|
|
|
describe('dynamically adding and removing nodes', function() {
|
|
beforeEach(function() {
|
|
this.focusGroup = createFocusGroup().activate();
|
|
});
|
|
|
|
afterEach(function() {
|
|
this.focusGroup.deactivate();
|
|
});
|
|
|
|
it('does nothing without nodes', function() {
|
|
assert.deepEqual(this.focusGroup.getMembers(), []);
|
|
nodeOne.focus();
|
|
simulateKeydown(arrowDownEvent);
|
|
assertActiveElement(nodeOne);
|
|
simulateKeydown({ keyCode: 84 }); // "t"
|
|
assertActiveElement(nodeOne);
|
|
});
|
|
|
|
it('works after adding nodes one at a time with addMember()', function() {
|
|
assert.deepEqual(this.focusGroup.getMembers(), []);
|
|
this.focusGroup.addMember(nodeOne);
|
|
this.focusGroup.addMember(nodeTwo);
|
|
assert.deepEqual(this.focusGroup.getMembers(), [
|
|
{ node: nodeOne, text: 'one' },
|
|
{ node: nodeTwo, text: 'two' },
|
|
]);
|
|
nodeOne.focus();
|
|
simulateKeydown(arrowDownEvent);
|
|
assertActiveElement(nodeTwo);
|
|
});
|
|
|
|
it('works after adding all nodels with setMembers()', function() {
|
|
assert.deepEqual(this.focusGroup.getMembers(), []);
|
|
this.focusGroup.setMembers([nodeThree, nodeFour]);
|
|
assert.deepEqual(this.focusGroup.getMembers(), [
|
|
{ node: nodeThree, text: 'three' },
|
|
{ node: nodeFour, text: '' },
|
|
]);
|
|
nodeThree.focus();
|
|
simulateKeydown(arrowDownEvent);
|
|
assertActiveElement(nodeFour);
|
|
});
|
|
|
|
it('adds a member at an index with addMember(node, index)', function() {
|
|
this.focusGroup.setMembers([nodeThree, nodeFour]);
|
|
this.focusGroup.addMember(nodeOne, 0);
|
|
this.focusGroup.addMember(nodeTwo, 1);
|
|
assert.deepEqual(this.focusGroup.getMembers(), [
|
|
{ node: nodeOne, text: 'one' },
|
|
{ node: nodeTwo, text: 'two' },
|
|
{ node: nodeThree, text: 'three' },
|
|
{ node: nodeFour, text: "" },
|
|
]);
|
|
});
|
|
|
|
it('removes a member with removeMember(node)', function() {
|
|
this.focusGroup.setMembers([nodeThree, nodeFour]);
|
|
this.focusGroup.removeMember(nodeThree);
|
|
assert.deepEqual(this.focusGroup.getMembers(), [
|
|
{ node: nodeFour, text: "" },
|
|
]);
|
|
});
|
|
|
|
it('removes a member with removeMember(index)', function() {
|
|
this.focusGroup.setMembers([nodeThree, nodeFour]);
|
|
this.focusGroup.removeMember(1);
|
|
assert.deepEqual(this.focusGroup.getMembers(), [
|
|
{ node: nodeThree, text: 'three' },
|
|
]);
|
|
});
|
|
|
|
it('does nothing when you remove a member that does not exist', function() {
|
|
this.focusGroup.setMembers([nodeThree]);
|
|
this.focusGroup.removeMember(nodeOne);
|
|
assert.deepEqual(this.focusGroup.getMembers(), [
|
|
{ node: nodeThree, text: "three" },
|
|
]);
|
|
});
|
|
});
|
|
|
|
describe('works while adding and removing nodes at whim', function() {
|
|
before(function() {
|
|
this.focusGroup = createFocusGroup().activate()
|
|
});
|
|
|
|
after(function() {
|
|
this.focusGroup.deactivate();
|
|
});
|
|
|
|
it('starts with an empty group', function() {
|
|
assert.deepEqual(this.focusGroup.getMembers(), []);
|
|
});
|
|
|
|
it('adds members with setMembers()', function() {
|
|
this.focusGroup.setMembers([nodeOne, nodeTwo, nodeThree, nodeFour]);
|
|
assert.deepEqual(this.focusGroup.getMembers(), [
|
|
{ node: nodeOne, text: 'one' },
|
|
{ node: nodeTwo, text: 'two' },
|
|
{ node: nodeThree, text: 'three' },
|
|
{ node: nodeFour, text: '' },
|
|
]);
|
|
});
|
|
|
|
it('removes a member with removeMember(node)', function() {
|
|
this.focusGroup.removeMember(nodeTwo);
|
|
assert.deepEqual(this.focusGroup.getMembers(), [
|
|
{ node: nodeOne, text: 'one' },
|
|
{ node: nodeThree, text: 'three' },
|
|
{ node: nodeFour, text: '' },
|
|
]);
|
|
});
|
|
|
|
it('still registers proper focus movement', function() {
|
|
nodeOne.focus();
|
|
simulateKeydown(arrowDownEvent);
|
|
assertActiveElement(nodeThree);
|
|
});
|
|
|
|
it('clears members with clearMembers()', function() {
|
|
this.focusGroup.clearMembers();
|
|
assert.deepEqual(this.focusGroup.getMembers(), []);
|
|
});
|
|
|
|
it('still registers proper focus movement', function() {
|
|
simulateKeydown(arrowDownEvent);
|
|
assertActiveElement(nodeThree);
|
|
});
|
|
});
|
|
|
|
describe('when an object without a focus() method is added to the group', function() {
|
|
it('throws an error', function() {
|
|
assert.throws(function() {
|
|
createFocusGroup({ members: [nodeOne, nodeTwo, { foo: 'bar' }] }).activate()
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('focusNodeAtIndex', function() {
|
|
beforeEach(function() {
|
|
this.focusGroup = createFocusGroup({ members: [nodeOne, nodeTwo, nodeThree, nodeFour] })
|
|
.activate();
|
|
});
|
|
|
|
afterEach(function() {
|
|
this.focusGroup.deactivate();
|
|
});
|
|
|
|
it('works', function() {
|
|
nodeOne.focus();
|
|
this.focusGroup.focusNodeAtIndex(2);
|
|
assertActiveElement(nodeThree);
|
|
this.focusGroup.focusNodeAtIndex(0);
|
|
assertActiveElement(nodeOne);
|
|
this.focusGroup.focusNodeAtIndex(3);
|
|
assertActiveElement(nodeFour);
|
|
});
|
|
|
|
it('quietly fails to focus when non-existant index is passed', function() {
|
|
nodeOne.focus();
|
|
this.focusGroup.focusNodeAtIndex(6);
|
|
assertActiveElement(nodeOne);
|
|
});
|
|
});
|
|
|
|
function assertActiveElement(node, message) {
|
|
assert.equal(document.activeElement, node, message);
|
|
}
|
|
|
|
function simulateKeydown(mockEvent) {
|
|
simulant.fire(document, 'keydown', mockEvent);
|
|
}
|