mirror of
https://github.com/Choices-js/Choices.git
synced 2024-06-08 08:52:19 +02:00
Easier to understand focus/blur events
This commit is contained in:
parent
2c4e86ddc0
commit
df8c1bfcb0
|
@ -367,7 +367,7 @@ export default class Choices {
|
||||||
* @return {Object} Class instance
|
* @return {Object} Class instance
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
showDropdown() {
|
showDropdown(focusInput) {
|
||||||
const body = document.body;
|
const body = document.body;
|
||||||
const html = document.documentElement;
|
const html = document.documentElement;
|
||||||
const winHeight = Math.max(body.scrollHeight, body.offsetHeight, html.clientHeight, html.scrollHeight, html.offsetHeight);
|
const winHeight = Math.max(body.scrollHeight, body.offsetHeight, html.clientHeight, html.scrollHeight, html.offsetHeight);
|
||||||
|
@ -387,6 +387,10 @@ export default class Choices {
|
||||||
this.containerOuter.classList.remove(this.config.classNames.flippedState);
|
this.containerOuter.classList.remove(this.config.classNames.flippedState);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (focusInput && this.canSearch) {
|
||||||
|
this.input.focus();
|
||||||
|
}
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1315,27 +1319,54 @@ export default class Choices {
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_onFocus(e) {
|
_onFocus(e) {
|
||||||
const target = e.target || e.touches[0].target;
|
const target = e.target;
|
||||||
const hasActiveDropdown = this.dropdown.classList.contains(this.config.classNames.activeState);
|
const hasActiveDropdown = this.dropdown.classList.contains(this.config.classNames.activeState);
|
||||||
|
|
||||||
if (target === this.input && !hasActiveDropdown) {
|
if (!hasActiveDropdown) {
|
||||||
this.containerOuter.classList.add(this.config.classNames.focusState);
|
switch (this.passedElement.type) {
|
||||||
if (this.passedElement.type === 'select-one' || this.passedElement.type === 'select-multiple') {
|
case 'text': {
|
||||||
this.showDropdown();
|
if (target === this.input) {
|
||||||
}
|
this.containerOuter.classList.add(this.config.classNames.focusState);
|
||||||
} else if (this.passedElement.type !== 'text' && (target === this.containerOuter || target === this.containerInner) && !hasActiveDropdown) {
|
}
|
||||||
// If element is a select box, the focussed element is the container and the dropdown
|
|
||||||
// isn't already open, focus and show dropdown
|
|
||||||
this.containerOuter.classList.add(this.config.classNames.focusState);
|
|
||||||
this.showDropdown();
|
|
||||||
|
|
||||||
if (this.passedElement.type === 'select-one' && target === this.containerOuter) {
|
break;
|
||||||
if (!this.focusAndHideDropdown) {
|
|
||||||
this.input.focus();
|
|
||||||
}
|
}
|
||||||
this.focusAndHideDropdown = false;
|
case 'select-one': {
|
||||||
} else if (this.canSearch) {
|
if (target === this.containerOuter) {
|
||||||
this.input.focus();
|
// If element is a select box, the focussed element is the container and the dropdown
|
||||||
|
// isn't already open, focus and show dropdown
|
||||||
|
this.containerOuter.classList.add(this.config.classNames.focusState);
|
||||||
|
this.showDropdown();
|
||||||
|
|
||||||
|
if (!this.focusAndHideDropdown && this.canSearch) {
|
||||||
|
this.input.focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.focusAndHideDropdown = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (target === this.input) {
|
||||||
|
// If element is a select box, the focussed element is the container and the dropdown
|
||||||
|
// isn't already open, focus and show dropdown
|
||||||
|
this.containerOuter.classList.add(this.config.classNames.focusState);
|
||||||
|
this.showDropdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'select-multiple': {
|
||||||
|
if (target === this.input) {
|
||||||
|
// If element is a select box, the focussed element is the container and the dropdown
|
||||||
|
// isn't already open, focus and show dropdown
|
||||||
|
this.containerOuter.classList.add(this.config.classNames.focusState);
|
||||||
|
this.showDropdown(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1347,25 +1378,60 @@ export default class Choices {
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_onBlur(e) {
|
_onBlur(e) {
|
||||||
// If the blurred element is this input or the outer container
|
const target = e.target;
|
||||||
if (e.target === this.input || (e.target === this.containerOuter && this.passedElement.type === 'select-one')) {
|
const activeItems = this.store.getItemsFilteredByActive();
|
||||||
const activeItems = this.store.getItemsFilteredByActive();
|
const hasActiveDropdown = this.dropdown.classList.contains(this.config.classNames.activeState);
|
||||||
const hasActiveDropdown = this.dropdown.classList.contains(this.config.classNames.activeState);
|
const hasHighlightedItems = activeItems.some((item) => item.highlighted === true);
|
||||||
const hasHighlightedItems = activeItems.some((item) => item.highlighted === true);
|
|
||||||
|
|
||||||
// Remove the focus state
|
switch (this.passedElement.type) {
|
||||||
this.containerOuter.classList.remove(this.config.classNames.focusState);
|
case 'text': {
|
||||||
|
if (target === this.input) {
|
||||||
|
// Remove the focus state
|
||||||
|
this.containerOuter.classList.remove(this.config.classNames.focusState);
|
||||||
|
// De-select any highlighted items
|
||||||
|
if (hasHighlightedItems) {
|
||||||
|
this.unhighlightAll();
|
||||||
|
}
|
||||||
|
if (hasActiveDropdown) {
|
||||||
|
this.hideDropdown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Close the dropdown if it is active, the input is the target (select-multiple, text, select-one (with search))
|
break;
|
||||||
// or the outer container is the target with no search (select-one)
|
}
|
||||||
if (hasActiveDropdown && (e.target === this.input || (e.target === this.containerOuter && !this.canSearch))) {
|
case 'select-one': {
|
||||||
this.hideDropdown();
|
if (target === this.containerOuter) {
|
||||||
|
if (hasActiveDropdown && (this.focusAndHideDropdown || !this.config.search)) {
|
||||||
|
this.hideDropdown();
|
||||||
|
}
|
||||||
|
this.containerOuter.classList.remove(this.config.classNames.focusState);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (target === this.input) {
|
||||||
|
this.hideDropdown();
|
||||||
|
this.containerOuter.classList.remove(this.config.classNames.focusState);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'select-multiple': {
|
||||||
|
if (target === this.input) {
|
||||||
|
// Remove the focus state
|
||||||
|
this.containerOuter.classList.remove(this.config.classNames.focusState);
|
||||||
|
if (hasActiveDropdown) {
|
||||||
|
this.hideDropdown();
|
||||||
|
}
|
||||||
|
// De-select any highlighted items
|
||||||
|
if (hasHighlightedItems) {
|
||||||
|
this.unhighlightAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// De-select any highlighted items
|
default:
|
||||||
if (hasHighlightedItems) {
|
break;
|
||||||
this.unhighlightAll();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,7 @@ describe('Choices', function() {
|
||||||
this.input.className = 'js-choices';
|
this.input.className = 'js-choices';
|
||||||
|
|
||||||
document.body.appendChild(this.input);
|
document.body.appendChild(this.input);
|
||||||
|
|
||||||
this.choices = new Choices(this.input);
|
this.choices = new Choices(this.input);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@ describe('Choices', function() {
|
||||||
|
|
||||||
it('should not re-initialise if passed element again', function() {
|
it('should not re-initialise if passed element again', function() {
|
||||||
const reinitialise = new Choices(this.choices.passedElement);
|
const reinitialise = new Choices(this.choices.passedElement);
|
||||||
spyOn(reinitialise, '_createTemplates');
|
spyOn(reinitialise, '_createTemplates');
|
||||||
expect(reinitialise._createTemplates).not.toHaveBeenCalled();
|
expect(reinitialise._createTemplates).not.toHaveBeenCalled();
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -132,7 +132,7 @@ describe('Choices', function() {
|
||||||
this.input.placeholder = 'Placeholder text';
|
this.input.placeholder = 'Placeholder text';
|
||||||
|
|
||||||
document.body.appendChild(this.input);
|
document.body.appendChild(this.input);
|
||||||
|
|
||||||
this.choices = new Choices(this.input);
|
this.choices = new Choices(this.input);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -165,12 +165,12 @@ describe('Choices', function() {
|
||||||
|
|
||||||
option.value = `Value ${i}`;
|
option.value = `Value ${i}`;
|
||||||
option.innerHTML = `Value ${i}`;
|
option.innerHTML = `Value ${i}`;
|
||||||
|
|
||||||
this.input.appendChild(option);
|
this.input.appendChild(option);
|
||||||
}
|
}
|
||||||
|
|
||||||
document.body.appendChild(this.input);
|
document.body.appendChild(this.input);
|
||||||
|
|
||||||
this.choices = new Choices(this.input);
|
this.choices = new Choices(this.input);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -179,7 +179,7 @@ describe('Choices', function() {
|
||||||
expect(this.choices.dropdown.classList).toContain(this.choices.config.classNames.activeState);
|
expect(this.choices.dropdown.classList).toContain(this.choices.config.classNames.activeState);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should select the first choice', function() {
|
it('should select the first choice', function() {
|
||||||
expect(this.choices.currentState.items[0].value).toContain('Value 1');
|
expect(this.choices.currentState.items[0].value).toContain('Value 1');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -209,7 +209,7 @@ describe('Choices', function() {
|
||||||
ctrlKey: false,
|
ctrlKey: false,
|
||||||
preventDefault: () => {}
|
preventDefault: () => {}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Key down to select choice
|
// Key down to select choice
|
||||||
this.choices._onKeyDown({
|
this.choices._onKeyDown({
|
||||||
target: this.choices.input,
|
target: this.choices.input,
|
||||||
|
@ -221,7 +221,7 @@ describe('Choices', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should trigger a change callback on selection', function() {
|
it('should trigger a change callback on selection', function() {
|
||||||
spyOn(this.choices.config, 'callbackOnChange');
|
spyOn(this.choices.config, 'callbackOnChange');
|
||||||
this.choices.input.focus();
|
this.choices.input.focus();
|
||||||
|
|
||||||
// Key down to second choice
|
// Key down to second choice
|
||||||
|
@ -231,7 +231,7 @@ describe('Choices', function() {
|
||||||
ctrlKey: false,
|
ctrlKey: false,
|
||||||
preventDefault: () => {}
|
preventDefault: () => {}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Key down to select choice
|
// Key down to select choice
|
||||||
this.choices._onKeyDown({
|
this.choices._onKeyDown({
|
||||||
target: this.choices.input,
|
target: this.choices.input,
|
||||||
|
@ -300,15 +300,15 @@ describe('Choices', function() {
|
||||||
option.value = `Value ${i}`;
|
option.value = `Value ${i}`;
|
||||||
option.innerHTML = `Value ${i}`;
|
option.innerHTML = `Value ${i}`;
|
||||||
|
|
||||||
if(i % 2) {
|
if(i % 2) {
|
||||||
option.selected = true;
|
option.selected = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.input.appendChild(option);
|
this.input.appendChild(option);
|
||||||
}
|
}
|
||||||
|
|
||||||
document.body.appendChild(this.input);
|
document.body.appendChild(this.input);
|
||||||
|
|
||||||
this.choices = new Choices(this.input, {
|
this.choices = new Choices(this.input, {
|
||||||
placeholderValue: 'Placeholder text',
|
placeholderValue: 'Placeholder text',
|
||||||
choices: [
|
choices: [
|
||||||
|
@ -345,10 +345,10 @@ describe('Choices', function() {
|
||||||
option.value = `Value ${i}`;
|
option.value = `Value ${i}`;
|
||||||
option.innerHTML = `Value ${i}`;
|
option.innerHTML = `Value ${i}`;
|
||||||
|
|
||||||
if(i % 2) {
|
if(i % 2) {
|
||||||
option.selected = true;
|
option.selected = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.input.appendChild(option);
|
this.input.appendChild(option);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -423,7 +423,7 @@ describe('Choices', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle toggleDropdown()', function() {
|
it('should handle toggleDropdown()', function() {
|
||||||
spyOn(this.choices, 'hideDropdown');
|
spyOn(this.choices, 'hideDropdown');
|
||||||
this.choices.showDropdown();
|
this.choices.showDropdown();
|
||||||
this.choices.toggleDropdown();
|
this.choices.toggleDropdown();
|
||||||
expect(this.choices.hideDropdown).toHaveBeenCalled();
|
expect(this.choices.hideDropdown).toHaveBeenCalled();
|
||||||
|
@ -476,7 +476,7 @@ describe('Choices', function() {
|
||||||
{value: 'Child Two', label: 'Child Two', disabled: true},
|
{value: 'Child Two', label: 'Child Two', disabled: true},
|
||||||
{value: 'Child Three', label: 'Child Three'},
|
{value: 'Child Three', label: 'Child Three'},
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Group two',
|
label: 'Group two',
|
||||||
id: 2,
|
id: 2,
|
||||||
|
@ -487,8 +487,8 @@ describe('Choices', function() {
|
||||||
{value: 'Child Six', label: 'Child Six'},
|
{value: 'Child Six', label: 'Child Six'},
|
||||||
]
|
]
|
||||||
}], 'value', 'label');
|
}], 'value', 'label');
|
||||||
|
|
||||||
|
|
||||||
const groups = this.choices.currentState.groups;
|
const groups = this.choices.currentState.groups;
|
||||||
const choices = this.choices.currentState.choices;
|
const choices = this.choices.currentState.choices;
|
||||||
|
|
||||||
|
@ -523,7 +523,7 @@ describe('Choices', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle ajax()', function() {
|
it('should handle ajax()', function() {
|
||||||
spyOn(this.choices, 'ajax');
|
spyOn(this.choices, 'ajax');
|
||||||
|
|
||||||
this.choices.ajax((callback) => {
|
this.choices.ajax((callback) => {
|
||||||
fetch('https://restcountries.eu/rest/v1/all')
|
fetch('https://restcountries.eu/rest/v1/all')
|
||||||
|
@ -536,7 +536,7 @@ describe('Choices', function() {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(this.choices.ajax).toHaveBeenCalledWith(jasmine.any(Function));
|
expect(this.choices.ajax).toHaveBeenCalledWith(jasmine.any(Function));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -562,7 +562,7 @@ describe('Choices', function() {
|
||||||
const randomItem = items[Math.floor(Math.random()*items.length)];
|
const randomItem = items[Math.floor(Math.random()*items.length)];
|
||||||
|
|
||||||
this.choices.removeItemsByValue(randomItem.value);
|
this.choices.removeItemsByValue(randomItem.value);
|
||||||
|
|
||||||
expect(randomItem.active).toBe(false);
|
expect(randomItem.active).toBe(false);
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue