mirror of
https://github.com/Choices-js/Choices.git
synced 2024-05-10 17:46:35 +02:00
Merge branch 'master' into feature/select-placeholders
* master: (48 commits) select-one input is now not focusable in disabled state (fixes #137) Don't use template literals on demo Use nornmal functions on demo Update README.md Version 2.7.7 Resolve broken cancel buttons Resolve ARIA role bug Update README.md naming change lint fixes Fix item custom template select bug Version 2.7.6 Update documentation Added showDropdown/hideDropdown to list of events Added showDropdown/hideDropdown events Version 2.7.5 Add label to highlighting events too Add example in index.html + refactoring Adding generated sources Add feature of passing the label through the event of add/remove item Fix choices crash with wicked_pdf ... # Conflicts: # assets/scripts/src/choices.js # index.html # tests/spec/choices_spec.js
This commit is contained in:
commit
4a8cdecbfd
57
README.md
57
README.md
|
@ -1,4 +1,4 @@
|
|||
# Choices.js [![Build Status](https://travis-ci.org/jshjohnson/Choices.svg?branch=master)](https://travis-ci.org/jshjohnson/Choices)
|
||||
# Choices.js ![Build Status](https://travis-ci.org/jshjohnson/Choices.svg?branch=master)
|
||||
A vanilla, lightweight (~15kb gzipped 🎉), configurable select box/text input plugin. Similar to Select2 and Selectize but without the jQuery dependency.
|
||||
|
||||
[Demo](https://joshuajohnson.co.uk/Choices/)
|
||||
|
@ -13,6 +13,10 @@ A vanilla, lightweight (~15kb gzipped 🎉), configurable select box/text input
|
|||
* Right-to-left support
|
||||
* Custom templates
|
||||
|
||||
----
|
||||
### Interested in writing your own ES6 JavaScript plugins? Check out [ES6.io](https://ES6.io/friend/JOHNSON) for great tutorials! 💪🏼
|
||||
----
|
||||
|
||||
## Installation
|
||||
With [NPM](https://www.npmjs.com/package/choices.js):
|
||||
```zsh
|
||||
|
@ -64,7 +68,8 @@ Or include Choices directly:
|
|||
paste: true,
|
||||
search: true,
|
||||
searchFloor: 1,
|
||||
flip: true,
|
||||
position: 'auto',
|
||||
resetScrollPosition: true,
|
||||
regexFilter: null,
|
||||
shouldSort: true,
|
||||
sortFilter: () => {...},
|
||||
|
@ -246,12 +251,19 @@ Pass an array of objects:
|
|||
|
||||
**Usage:** The minimum length a search value should be before choices are searched.
|
||||
|
||||
### flip
|
||||
**Type:** `Boolean` **Default:** `true`
|
||||
### position
|
||||
**Type:** `String` **Default:** `auto`
|
||||
|
||||
**Input types affected:** `select-one`, `select-multiple`
|
||||
|
||||
**Usage:** Whether the dropdown should appear above the input (rather than beneath) if there is not enough space within the window.
|
||||
**Usage:** Whether the dropdown should appear above (`top`) or below (`bottom`) the input. By default, if there is not enough space within the window the dropdown will appear above the input, otherwise below it.
|
||||
|
||||
### resetScrollPosition
|
||||
**Type:** `Boolean` **Default:** `true`
|
||||
|
||||
**Input types affected:** `select-multiple`
|
||||
|
||||
**Usage:** Whether the scroll position should reset after adding an item.
|
||||
|
||||
### regexFilter
|
||||
**Type:** `Regex` **Default:** `null`
|
||||
|
@ -332,18 +344,18 @@ const example = new Choices(element, {
|
|||
**Usage:** The text that is shown whilst choices are being populated via AJAX.
|
||||
|
||||
### noResultsText
|
||||
**Type:** `String` **Default:** `No results found`
|
||||
**Type:** `String/Function` **Default:** `No results found`
|
||||
|
||||
**Input types affected:** `select-one`, `select-multiple`
|
||||
|
||||
**Usage:** The text that is shown when a user's search has returned no results.
|
||||
**Usage:** The text that is shown when a user's search has returned no results. Optionally pass a function returning a string.
|
||||
|
||||
### noChoicesText
|
||||
**Type:** `String` **Default:** `No choices to choose from`
|
||||
**Type:** `String/Function` **Default:** `No choices to choose from`
|
||||
|
||||
**Input types affected:** `select-multiple`
|
||||
|
||||
**Usage:** The text that is shown when a user has selected all possible choices.
|
||||
**Usage:** The text that is shown when a user has selected all possible choices. Optionally pass a function returning a string.
|
||||
|
||||
### itemSelectText
|
||||
**Type:** `String` **Default:** `Press to select`
|
||||
|
@ -457,6 +469,7 @@ element.addEventListener('addItem', function(event) {
|
|||
// do something creative here...
|
||||
console.log(event.detail.id);
|
||||
console.log(event.detail.value);
|
||||
console.log(event.detail.label);
|
||||
console.log(event.detail.groupValue);
|
||||
}, false);
|
||||
|
||||
|
@ -467,39 +480,47 @@ example.passedElement.addEventListener('addItem', function(event) {
|
|||
// do something creative here...
|
||||
console.log(event.detail.id);
|
||||
console.log(event.detail.value);
|
||||
console.log(event.detail.label);
|
||||
console.log(event.detail.groupValue);
|
||||
}, false);
|
||||
|
||||
```
|
||||
|
||||
### addItem
|
||||
**Arguments:** `id, value, groupValue`
|
||||
**Arguments:** `id, value, label, groupValue`
|
||||
|
||||
**Input types affected:** `text`, `select-one`, `select-multiple`
|
||||
|
||||
**Usage:** Triggered each time an item is added (programmatically or by the user).
|
||||
|
||||
### removeItem
|
||||
**Arguments:** `id, value, groupValue`
|
||||
**Arguments:** `id, value, label, groupValue`
|
||||
|
||||
**Input types affected:** `text`, `select-one`, `select-multiple`
|
||||
|
||||
**Usage:** Triggered each time an item is removed (programmatically or by the user).
|
||||
|
||||
### highlightItem
|
||||
**Arguments:** `id, value, groupValue`
|
||||
**Arguments:** `id, value, label, groupValue`
|
||||
|
||||
**Input types affected:** `text`, `select-multiple`
|
||||
|
||||
**Usage:** Triggered each time an item is highlighted.
|
||||
|
||||
### unhighlightItem
|
||||
**Arguments:** `id, value, groupValue`
|
||||
**Arguments:** `id, value, label, groupValue`
|
||||
|
||||
**Input types affected:** `text`, `select-multiple`
|
||||
|
||||
**Usage:** Triggered each time an item is unhighlighted.
|
||||
|
||||
### choice
|
||||
**Arguments:** `value`
|
||||
|
||||
**Input types affected:** `select-one`, `select-multiple`
|
||||
|
||||
**Usage:** Triggered each time a choice is selected **by a user**, regardless if it changes the value of the input.
|
||||
|
||||
### change
|
||||
**Arguments:** `value`
|
||||
|
||||
|
@ -512,6 +533,16 @@ example.passedElement.addEventListener('addItem', function(event) {
|
|||
|
||||
**Usage:** Triggered when a user types into an input to search choices.
|
||||
|
||||
### showDropdown
|
||||
**Arguments:** - **Input types affected:** `select-one`, `select-multiple`
|
||||
|
||||
**Usage:** Triggered when the dropdown is shown.
|
||||
|
||||
### hideDropdown
|
||||
**Arguments:** - **Input types affected:** `select-one`, `select-multiple`
|
||||
|
||||
**Usage:** Triggered when the dropdown is hidden.
|
||||
|
||||
## Methods
|
||||
Methods can be called either directly or by chaining:
|
||||
|
||||
|
|
1007
assets/scripts/dist/choices.js
vendored
1007
assets/scripts/dist/choices.js
vendored
File diff suppressed because it is too large
Load diff
2
assets/scripts/dist/choices.js.map
vendored
2
assets/scripts/dist/choices.js.map
vendored
File diff suppressed because one or more lines are too long
6
assets/scripts/dist/choices.min.js
vendored
6
assets/scripts/dist/choices.min.js
vendored
File diff suppressed because one or more lines are too long
|
@ -16,6 +16,7 @@ import {
|
|||
isScrolledIntoView,
|
||||
getAdjacentEl,
|
||||
wrap,
|
||||
getType,
|
||||
isType,
|
||||
isElement,
|
||||
strToEl,
|
||||
|
@ -24,6 +25,7 @@ import {
|
|||
sortByAlpha,
|
||||
sortByScore,
|
||||
triggerEvent,
|
||||
findAncestorByAttrName
|
||||
}
|
||||
from './lib/utils.js';
|
||||
import './lib/polyfills.js';
|
||||
|
@ -61,6 +63,8 @@ class Choices {
|
|||
searchFloor: 1,
|
||||
searchPlaceholderValue: null,
|
||||
flip: true,
|
||||
position: 'auto',
|
||||
resetScrollPosition: true,
|
||||
regexFilter: null,
|
||||
shouldSort: true,
|
||||
sortFilter: sortByAlpha,
|
||||
|
@ -389,8 +393,11 @@ class Choices {
|
|||
|
||||
// Clear choices
|
||||
this.choiceList.innerHTML = '';
|
||||
|
||||
// Scroll back to top of choices list
|
||||
this.choiceList.scrollTop = 0;
|
||||
if(this.config.resetScrollPosition){
|
||||
this.choiceList.scrollTop = 0;
|
||||
}
|
||||
|
||||
// If we have grouped options
|
||||
if (activeGroups.length >= 1 && this.isSearching !== true) {
|
||||
|
@ -406,9 +413,17 @@ class Choices {
|
|||
this._highlightChoice();
|
||||
} else {
|
||||
// Otherwise show a notice
|
||||
const dropdownItem = this.isSearching ?
|
||||
this._getTemplate('notice', this.config.noResultsText) :
|
||||
this._getTemplate('notice', this.config.noChoicesText);
|
||||
let dropdownItem;
|
||||
let notice;
|
||||
|
||||
if (this.isSearching) {
|
||||
notice = isType('Function', this.config.noResultsText) ? this.config.noResultsText() : this.config.noResultsText;
|
||||
dropdownItem = this._getTemplate('notice', notice);
|
||||
} else {
|
||||
notice = isType('Function', this.config.noChoicesText) ? this.config.noChoicesText() : this.config.noChoicesText;
|
||||
dropdownItem = this._getTemplate('notice', notice);
|
||||
}
|
||||
|
||||
this.choiceList.appendChild(dropdownItem);
|
||||
}
|
||||
}
|
||||
|
@ -461,12 +476,14 @@ class Choices {
|
|||
triggerEvent(this.passedElement, 'highlightItem', {
|
||||
id,
|
||||
value: item.value,
|
||||
label: item.label,
|
||||
groupValue: group.value
|
||||
});
|
||||
} else {
|
||||
triggerEvent(this.passedElement, 'highlightItem', {
|
||||
id,
|
||||
value: item.value,
|
||||
label: item.label,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -492,12 +509,14 @@ class Choices {
|
|||
triggerEvent(this.passedElement, 'unhighlightItem', {
|
||||
id,
|
||||
value: item.value,
|
||||
label: item.label,
|
||||
groupValue: group.value
|
||||
});
|
||||
} else {
|
||||
triggerEvent(this.passedElement, 'unhighlightItem', {
|
||||
id,
|
||||
value: item.value,
|
||||
label: item.label,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -605,17 +624,29 @@ class Choices {
|
|||
showDropdown(focusInput = false) {
|
||||
const body = document.body;
|
||||
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
|
||||
);
|
||||
|
||||
this.containerOuter.classList.add(this.config.classNames.openState);
|
||||
this.containerOuter.setAttribute('aria-expanded', 'true');
|
||||
this.dropdown.classList.add(this.config.classNames.activeState);
|
||||
this.dropdown.setAttribute('aria-expanded', 'true');
|
||||
|
||||
const dimensions = this.dropdown.getBoundingClientRect();
|
||||
const dropdownPos = Math.ceil(dimensions.top + window.scrollY + dimensions.height);
|
||||
|
||||
// If flip is enabled and the dropdown bottom position is greater than the window height flip the dropdown.
|
||||
const shouldFlip = this.config.flip ? dropdownPos >= winHeight : false;
|
||||
let shouldFlip = false;
|
||||
if (this.config.position === 'auto') {
|
||||
shouldFlip = dropdownPos >= winHeight;
|
||||
} else if (this.config.position === 'top') {
|
||||
shouldFlip = true;
|
||||
}
|
||||
|
||||
if (shouldFlip) {
|
||||
this.containerOuter.classList.add(this.config.classNames.flippedState);
|
||||
|
@ -628,6 +659,8 @@ class Choices {
|
|||
this.input.focus();
|
||||
}
|
||||
|
||||
triggerEvent(this.passedElement, 'showDropdown', {});
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -643,6 +676,7 @@ class Choices {
|
|||
this.containerOuter.classList.remove(this.config.classNames.openState);
|
||||
this.containerOuter.setAttribute('aria-expanded', 'false');
|
||||
this.dropdown.classList.remove(this.config.classNames.activeState);
|
||||
this.dropdown.setAttribute('aria-expanded', 'false');
|
||||
|
||||
if (isFlipped) {
|
||||
this.containerOuter.classList.remove(this.config.classNames.flippedState);
|
||||
|
@ -653,6 +687,8 @@ class Choices {
|
|||
this.input.blur();
|
||||
}
|
||||
|
||||
triggerEvent(this.passedElement, 'hideDropdown', {});
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -700,34 +736,42 @@ class Choices {
|
|||
/**
|
||||
* Set value of input. If the input is a select box, a choice will be created and selected otherwise
|
||||
* an item will created directly.
|
||||
* @param {Array} args Array of value objects or value strings
|
||||
* @param {Array} args Array of value objects or value strings
|
||||
* @return {Object} Class instance
|
||||
* @public
|
||||
*/
|
||||
setValue(args) {
|
||||
if (this.initialised === true) {
|
||||
// Convert args to an itterable array
|
||||
// Convert args to an iterable array
|
||||
const values = [...args],
|
||||
passedElementType = this.passedElement.type;
|
||||
passedElementType = this.passedElement.type,
|
||||
handleValue = (item) => {
|
||||
const itemType = getType(item);
|
||||
if (itemType === 'Object') {
|
||||
if (!item.value) return;
|
||||
// If we are dealing with a select input, we need to create an option first
|
||||
// that is then selected. For text inputs we can just add items normally.
|
||||
if (passedElementType !== 'text') {
|
||||
this._addChoice(true, false, item.value, item.label, -1);
|
||||
} else {
|
||||
this._addItem(item.value, item.label, item.id);
|
||||
}
|
||||
} else if (itemType === 'String') {
|
||||
if (passedElementType !== 'text') {
|
||||
this._addChoice(true, false, item, item, -1);
|
||||
} else {
|
||||
this._addItem(item);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
values.forEach((item) => {
|
||||
if (isType('Object', item)) {
|
||||
if (!item.value) return;
|
||||
// If we are dealing with a select input, we need to create an option first
|
||||
// that is then selected. For text inputs we can just add items normally.
|
||||
if (passedElementType !== 'text') {
|
||||
this._addChoice(true, false, item.value, item.label, -1);
|
||||
} else {
|
||||
this._addItem(item.value, item.label, item.id);
|
||||
}
|
||||
} else if (isType('String', item)) {
|
||||
if (passedElementType !== 'text') {
|
||||
this._addChoice(true, false, item, item, -1);
|
||||
} else {
|
||||
this._addItem(item);
|
||||
}
|
||||
}
|
||||
});
|
||||
if (values.length > 1) {
|
||||
values.forEach((value) => {
|
||||
handleValue(value);
|
||||
});
|
||||
} else {
|
||||
handleValue(values[0]);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
@ -791,7 +835,7 @@ class Choices {
|
|||
const isPlaceholder = result.placeholder ? result.placeholder : false;
|
||||
|
||||
if (result.choices) {
|
||||
this._addGroup(result, index, value, label);
|
||||
this._addGroup(result, (result.id || null), value, label);
|
||||
} else {
|
||||
this._addChoice(isSelected, isDisabled, result[value], result[label], -1, isPlaceholder);
|
||||
}
|
||||
|
@ -843,6 +887,9 @@ class Choices {
|
|||
this.input.removeAttribute('disabled');
|
||||
this.containerOuter.classList.remove(this.config.classNames.disabledState);
|
||||
this.containerOuter.removeAttribute('aria-disabled');
|
||||
if (this.passedElement.type === 'select-one') {
|
||||
this.containerOuter.setAttribute('tabindex', '0');
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
@ -861,6 +908,9 @@ class Choices {
|
|||
this.input.setAttribute('disabled', '');
|
||||
this.containerOuter.classList.add(this.config.classNames.disabledState);
|
||||
this.containerOuter.setAttribute('aria-disabled', 'true');
|
||||
if (this.passedElement.type === 'select-one') {
|
||||
this.containerOuter.setAttribute('tabindex', '-1');
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
@ -972,6 +1022,10 @@ class Choices {
|
|||
const choice = this.store.getChoiceById(id);
|
||||
const hasActiveDropdown = this.dropdown.classList.contains(this.config.classNames.activeState);
|
||||
|
||||
triggerEvent(this.passedElement, 'choice', {
|
||||
choice,
|
||||
});
|
||||
|
||||
if (choice && !choice.selected && !choice.disabled) {
|
||||
const canAddItem = this._canAddItem(activeItems, choice.value);
|
||||
|
||||
|
@ -1101,6 +1155,7 @@ class Choices {
|
|||
_ajaxCallback() {
|
||||
return (results, value, label) => {
|
||||
if (!results || !value) return;
|
||||
|
||||
const parsedResults = isType('Object', results) ? [results] : results;
|
||||
|
||||
if (parsedResults && isType('Array', parsedResults) && parsedResults.length) {
|
||||
|
@ -1112,12 +1167,16 @@ class Choices {
|
|||
const isDisabled = result.disabled ? result.disabled : false;
|
||||
const isPlaceholder = result.placeholder ? result.placeholder : false;
|
||||
if (result.choices) {
|
||||
this._addGroup(result, index, value, label);
|
||||
this._addGroup(result, (result.id || null), value, label);
|
||||
} else {
|
||||
this._addChoice(isSelected, isDisabled, result[value], result[label], isPlaceholder);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// No results, remove loading state
|
||||
this._handleLoadingState(false);
|
||||
}
|
||||
|
||||
this.containerOuter.removeAttribute('aria-busy');
|
||||
};
|
||||
}
|
||||
|
@ -1268,6 +1327,8 @@ class Choices {
|
|||
const escapeKey = 27;
|
||||
const upKey = 38;
|
||||
const downKey = 40;
|
||||
const pageUpKey = 33;
|
||||
const pageDownKey = 34;
|
||||
const ctrlDownKey = e.ctrlKey || e.metaKey;
|
||||
|
||||
// If a user is typing and the dropdown is not active
|
||||
|
@ -1342,16 +1403,25 @@ class Choices {
|
|||
this.showDropdown(true);
|
||||
}
|
||||
|
||||
const currentEl = this.dropdown.querySelector(`.${this.config.classNames.highlightedState}`);
|
||||
const directionInt = e.keyCode === downKey ? 1 : -1;
|
||||
let nextEl;
|
||||
|
||||
this.canSearch = false;
|
||||
|
||||
if (currentEl) {
|
||||
nextEl = getAdjacentEl(currentEl, '[data-choice-selectable]', directionInt);
|
||||
const directionInt = e.keyCode === downKey || e.keyCode === pageDownKey ? 1 : -1;
|
||||
const skipKey = e.metaKey || e.keyCode === pageDownKey || e.keyCode === pageUpKey;
|
||||
|
||||
let nextEl;
|
||||
if (skipKey) {
|
||||
if (directionInt > 0) {
|
||||
nextEl = Array.from(this.dropdown.querySelectorAll('[data-choice-selectable]')).pop();
|
||||
} else {
|
||||
nextEl = this.dropdown.querySelector('[data-choice-selectable]');
|
||||
}
|
||||
} else {
|
||||
nextEl = this.dropdown.querySelector('[data-choice-selectable]');
|
||||
const currentEl = this.dropdown.querySelector(`.${this.config.classNames.highlightedState}`);
|
||||
if (currentEl) {
|
||||
nextEl = getAdjacentEl(currentEl, '[data-choice-selectable]', directionInt);
|
||||
} else {
|
||||
nextEl = this.dropdown.querySelector('[data-choice-selectable]');
|
||||
}
|
||||
}
|
||||
|
||||
if (nextEl) {
|
||||
|
@ -1383,7 +1453,9 @@ class Choices {
|
|||
[enterKey]: onEnterKey,
|
||||
[escapeKey]: onEscapeKey,
|
||||
[upKey]: onDirectionKey,
|
||||
[pageUpKey]: onDirectionKey,
|
||||
[downKey]: onDirectionKey,
|
||||
[pageDownKey]: onDirectionKey,
|
||||
[deleteKey]: onDeleteKey,
|
||||
[backKey]: onDeleteKey,
|
||||
};
|
||||
|
@ -1511,13 +1583,16 @@ class Choices {
|
|||
_onMouseDown(e) {
|
||||
const target = e.target;
|
||||
if (this.containerOuter.contains(target) && target !== this.input) {
|
||||
let foundTarget;
|
||||
const activeItems = this.store.getItemsFilteredByActive();
|
||||
const hasShiftKey = e.shiftKey;
|
||||
|
||||
if (target.hasAttribute('data-item')) {
|
||||
this._handleItemAction(activeItems, target, hasShiftKey);
|
||||
} else if (target.hasAttribute('data-choice')) {
|
||||
this._handleChoiceAction(activeItems, target);
|
||||
if(foundTarget = findAncestorByAttrName(target, 'data-button')) {
|
||||
this._handleButtonAction(activeItems, foundTarget);
|
||||
} else if (foundTarget = findAncestorByAttrName(target, 'data-item')) {
|
||||
this._handleItemAction(activeItems, foundTarget, hasShiftKey);
|
||||
} else if (foundTarget = findAncestorByAttrName(target, 'data-choice')) {
|
||||
this._handleChoiceAction(activeItems, foundTarget);
|
||||
}
|
||||
|
||||
e.preventDefault();
|
||||
|
@ -1535,6 +1610,7 @@ class Choices {
|
|||
const hasActiveDropdown = this.dropdown.classList.contains(this.config.classNames.activeState);
|
||||
const activeItems = this.store.getItemsFilteredByActive();
|
||||
|
||||
|
||||
// If target is something that concerns us
|
||||
if (this.containerOuter.contains(target)) {
|
||||
// Handle button delete
|
||||
|
@ -1859,12 +1935,14 @@ class Choices {
|
|||
triggerEvent(this.passedElement, 'addItem', {
|
||||
id,
|
||||
value: passedValue,
|
||||
label: passedLabel,
|
||||
groupValue: group.value,
|
||||
});
|
||||
} else {
|
||||
triggerEvent(this.passedElement, 'addItem', {
|
||||
id,
|
||||
value: passedValue,
|
||||
label: passedLabel,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1886,6 +1964,7 @@ class Choices {
|
|||
|
||||
const id = item.id;
|
||||
const value = item.value;
|
||||
const label = item.label;
|
||||
const choiceId = item.choiceId;
|
||||
const groupId = item.groupId;
|
||||
const group = groupId >= 0 ? this.store.getGroupById(groupId) : null;
|
||||
|
@ -1896,12 +1975,14 @@ class Choices {
|
|||
triggerEvent(this.passedElement, 'removeItem', {
|
||||
id,
|
||||
value,
|
||||
label,
|
||||
groupValue: group.value,
|
||||
});
|
||||
} else {
|
||||
triggerEvent(this.passedElement, 'removeItem', {
|
||||
id,
|
||||
value,
|
||||
label,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1953,7 +2034,7 @@ class Choices {
|
|||
*/
|
||||
_addGroup(group, id, valueKey = 'value', labelKey = 'label') {
|
||||
const groupChoices = isType('Object', group) ? group.choices : Array.from(group.getElementsByTagName('OPTION'));
|
||||
const groupId = id;
|
||||
const groupId = id ? id : Math.floor(new Date().valueOf() * Math.random());
|
||||
const isDisabled = group.disabled ? group.disabled : false;
|
||||
|
||||
if (groupChoices) {
|
||||
|
@ -2241,8 +2322,8 @@ class Choices {
|
|||
this.isSearching = false;
|
||||
|
||||
if (passedGroups && passedGroups.length) {
|
||||
passedGroups.forEach((group, index) => {
|
||||
this._addGroup(group, index);
|
||||
passedGroups.forEach((group) => {
|
||||
this._addGroup(group, (group.id || null));
|
||||
});
|
||||
} else {
|
||||
const passedOptions = Array.from(this.passedElement.options);
|
||||
|
@ -2292,10 +2373,11 @@ class Choices {
|
|||
} else if (this.isTextElement) {
|
||||
// Add any preset values seperated by delimiter
|
||||
this.presetItems.forEach((item) => {
|
||||
if (isType('Object', item)) {
|
||||
const itemType = getType(item);
|
||||
if (itemType === 'Object') {
|
||||
if (!item.value) return;
|
||||
this._addItem(item.value, item.label, item.id);
|
||||
} else if (isType('String', item)) {
|
||||
} else if (itemType === 'String') {
|
||||
this._addItem(item);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -10,6 +10,16 @@ export const capitalise = function(str) {
|
|||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Tests the type of an object
|
||||
* @param {String} type Type to test object against
|
||||
* @param {Object} obj Object to be tested
|
||||
* @return {Boolean}
|
||||
*/
|
||||
export const getType = function(obj) {
|
||||
return Object.prototype.toString.call(obj).slice(8, -1);
|
||||
};
|
||||
|
||||
/**
|
||||
* Tests the type of an object
|
||||
* @param {String} type Type to test object against
|
||||
|
@ -17,7 +27,7 @@ export const capitalise = function(str) {
|
|||
* @return {Boolean}
|
||||
*/
|
||||
export const isType = function(type, obj) {
|
||||
var clas = Object.prototype.toString.call(obj).slice(8, -1);
|
||||
var clas = getType(obj);
|
||||
return obj !== undefined && obj !== null && clas === type;
|
||||
};
|
||||
|
||||
|
@ -30,7 +40,7 @@ export const isNode = (o) => {
|
|||
return (
|
||||
typeof Node === "object" ? o instanceof Node :
|
||||
o && typeof o === "object" && typeof o.nodeType === "number" && typeof o.nodeName === "string"
|
||||
);
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -42,7 +52,7 @@ export const isElement = (o) => {
|
|||
return (
|
||||
typeof HTMLElement === "object" ? o instanceof HTMLElement : //DOM2
|
||||
o && typeof o === "object" && o !== null && o.nodeType === 1 && typeof o.nodeName === "string"
|
||||
);
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -248,6 +258,26 @@ export const findAncestor = function(el, cls) {
|
|||
return el;
|
||||
};
|
||||
|
||||
/**
|
||||
* Find ancestor in DOM tree by attribute name
|
||||
* @param {NodeElement} el Element to start search from
|
||||
* @param {string} attr Attribute name of parent
|
||||
* @return {?NodeElement} Found parent element or null
|
||||
*/
|
||||
export const findAncestorByAttrName = function(el, attr) {
|
||||
let target = el;
|
||||
|
||||
while (target) {
|
||||
if (target.hasAttribute(attr)) {
|
||||
return target;
|
||||
}
|
||||
|
||||
target = target.parentElement;
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Debounce an event handler.
|
||||
* @param {Function} func Function to run after wait
|
||||
|
|
|
@ -138,4 +138,12 @@ h6, .h6 {
|
|||
display: none;
|
||||
}
|
||||
|
||||
.zero-bottom {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.zero-top {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
/*===== End of Section comment block ======*/
|
||||
|
|
2
assets/styles/css/base.min.css
vendored
2
assets/styles/css/base.min.css
vendored
|
@ -1 +1 @@
|
|||
*{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}*,:after,:before{box-sizing:border-box}body,html{position:relative;margin:0;width:100%;height:100%}body{font-family:"Helvetica Neue",Helvetica,Arial,"Lucida Grande",sans-serif;font-size:16px;line-height:1.4;color:#fff;background-color:#333;overflow-x:hidden}hr,label{display:block}label{margin-bottom:8px;font-size:14px;font-weight:500;cursor:pointer}p{margin-top:0}hr{margin:36px 0;border:0;border-bottom:1px solid #eaeaea;height:1px}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:12px;font-weight:400;line-height:1.2}a,a:focus,a:visited{color:#fff;text-decoration:none;font-weight:600}.form-control{display:block;width:100%;background-color:#f9f9f9;padding:12px;border:1px solid #ddd;border-radius:2.5px;font-size:14px;-webkit-appearance:none;-moz-appearance:none;appearance:none;margin-bottom:24px}.h1,h1{font-size:32px}.h2,h2{font-size:24px}.h3,h3{font-size:20px}.h4,h4{font-size:18px}.h5,h5{font-size:16px}.h6,h6{font-size:14px}.container{display:block;margin:auto;max-width:40em;padding:48px}@media (max-width:620px){.container{padding:0}}.section{background-color:#fff;padding:24px;color:#333}.section a,.section a:focus,.section a:visited{color:#00bcd4}.logo{display:block;margin-bottom:12px}.logo__img{width:100%;height:auto;display:inline-block;max-width:100%;vertical-align:top;padding:6px 0}.visible-ie{display:none}
|
||||
*{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}*,:after,:before{box-sizing:border-box}body,html{position:relative;margin:0;width:100%;height:100%}body{font-family:"Helvetica Neue",Helvetica,Arial,"Lucida Grande",sans-serif;font-size:16px;line-height:1.4;color:#fff;background-color:#333;overflow-x:hidden}hr,label{display:block}label{margin-bottom:8px;font-size:14px;font-weight:500;cursor:pointer}p{margin-top:0}hr{margin:36px 0;border:0;border-bottom:1px solid #eaeaea;height:1px}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:12px;font-weight:400;line-height:1.2}a,a:focus,a:visited{color:#fff;text-decoration:none;font-weight:600}.form-control{display:block;width:100%;background-color:#f9f9f9;padding:12px;border:1px solid #ddd;border-radius:2.5px;font-size:14px;-webkit-appearance:none;-moz-appearance:none;appearance:none;margin-bottom:24px}.h1,h1{font-size:32px}.h2,h2{font-size:24px}.h3,h3{font-size:20px}.h4,h4{font-size:18px}.h5,h5{font-size:16px}.h6,h6{font-size:14px}.container{display:block;margin:auto;max-width:40em;padding:48px}@media (max-width:620px){.container{padding:0}}.section{background-color:#fff;padding:24px;color:#333}.section a,.section a:focus,.section a:visited{color:#00bcd4}.logo{display:block;margin-bottom:12px}.logo__img{width:100%;height:auto;display:inline-block;max-width:100%;vertical-align:top;padding:6px 0}.visible-ie{display:none}.zero-bottom{margin-bottom:0}.zero-top{margin-top:0}
|
|
@ -46,7 +46,7 @@
|
|||
}
|
||||
|
||||
.choices[data-type*="select-one"] .choices__button {
|
||||
background-image: url("../../icons//cross-inverse.svg");
|
||||
background-image: url("../../icons/cross-inverse.svg");
|
||||
padding: 0;
|
||||
background-size: 8px;
|
||||
height: 100%;
|
||||
|
@ -113,7 +113,7 @@
|
|||
margin-left: 8px;
|
||||
padding-left: 16px;
|
||||
border-left: 1px solid #008fa1;
|
||||
background-image: url("../../icons//cross.svg");
|
||||
background-image: url("../../icons/cross.svg");
|
||||
background-size: 8px;
|
||||
width: 8px;
|
||||
line-height: 1;
|
||||
|
|
2
assets/styles/css/choices.min.css
vendored
2
assets/styles/css/choices.min.css
vendored
File diff suppressed because one or more lines are too long
|
@ -118,5 +118,7 @@ h6, .h6 { font-size: $global-font-size-h6; }
|
|||
}
|
||||
|
||||
.visible-ie { display: none; }
|
||||
.zero-bottom { margin-bottom: 0; }
|
||||
.zero-top { margin-top: 0; }
|
||||
|
||||
/*===== End of Section comment block ======*/
|
||||
|
|
|
@ -17,7 +17,7 @@ $choices-keyline-color: #DDDDDD !default;
|
|||
$choices-primary-color: #00BCD4 !default;
|
||||
$choices-disabled-color: #eaeaea !default;
|
||||
$choices-highlight-color: $choices-primary-color !default;
|
||||
$choices-button-icon-path: '../../icons/' !default;
|
||||
$choices-button-icon-path: '../../icons' !default;
|
||||
$choices-button-dimension: 8px !default;
|
||||
$choices-button-offset: 8px !default;
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "choices.js",
|
||||
"version": "2.6.1",
|
||||
"version": "2.7.7",
|
||||
"description": "A vanilla JS customisable text input/select box plugin",
|
||||
"main": [
|
||||
"./assets/scripts/dist/choices.js",
|
||||
|
@ -16,7 +16,7 @@
|
|||
"node_modules",
|
||||
"bower_components",
|
||||
"test",
|
||||
"tests",
|
||||
"tests"
|
||||
],
|
||||
"keywords": [
|
||||
"customisable",
|
||||
|
|
114
index.html
114
index.html
|
@ -15,7 +15,7 @@
|
|||
<meta name="theme-color" content="#ffffff">
|
||||
|
||||
<!-- Ignore these -->
|
||||
<link rel="stylesheet" href="assets/styles/css/base.min.css?version=2.6.1">
|
||||
<link rel="stylesheet" href="assets/styles/css/base.min.css?version=2.7.7">
|
||||
<!-- End ignore these -->
|
||||
|
||||
<!-- Optional includes -->
|
||||
|
@ -23,8 +23,8 @@
|
|||
<!-- End optional includes -->
|
||||
|
||||
<!-- Choices includes -->
|
||||
<link rel="stylesheet" href="assets/styles/css/choices.min.css?version=2.6.1">
|
||||
<script src="assets/scripts/dist/choices.min.js?version=2.6.1"></script>
|
||||
<link rel="stylesheet" href="assets/styles/css/choices.min.css?version=2.7.7">
|
||||
<script src="assets/scripts/dist/choices.min.js?version=2.7.7"></script>
|
||||
<!-- End Choices includes -->
|
||||
|
||||
<!--[if lt IE 9]>
|
||||
|
@ -137,6 +137,10 @@
|
|||
<option value="Dropdown item 4" disabled>Dropdown item 4</option>
|
||||
</select>
|
||||
|
||||
<label for="label-event">Use label in event (add/remove)</label>
|
||||
<p id="message"></p>
|
||||
<select id="choices-multiple-labels" multiple></select>
|
||||
|
||||
<hr>
|
||||
|
||||
<h2>Single select input</h2>
|
||||
|
@ -238,12 +242,12 @@
|
|||
<option value="three">Three</option>
|
||||
</select>
|
||||
|
||||
<label for="choices-custom-templates">Custom templates</label>
|
||||
<select class="form-control" name="choices-custom-templates" id="choices-custom-templates">
|
||||
<label for="choices-single-custom-templates">Custom templates</label>
|
||||
<select class="form-control" name="choices-single-custom-templates" id="choices-single-custom-templates" placeholder="This is a placeholder">
|
||||
<option value="React">React</option>
|
||||
<option value="React">Angular</option>
|
||||
<option value="React">Ember</option>
|
||||
<option value="React">Vue</option>
|
||||
<option value="Angular">Angular</option>
|
||||
<option value="Ember">Ember</option>
|
||||
<option value="Vue">Vue</option>
|
||||
</select>
|
||||
|
||||
<p>Below is an example of how you could have two select inputs depend on eachother. 'Boroughs' will only be enabled if the value of 'States' is 'New York'</p>
|
||||
|
@ -285,11 +289,12 @@
|
|||
paste: false,
|
||||
duplicateItems: false,
|
||||
editItems: true,
|
||||
addItemText: (value) => {
|
||||
return `Appuyez sur Entrée pour ajouter <b>"${value}"</b>`;
|
||||
maxItemCount: 5,
|
||||
addItemText: function(value) {
|
||||
return 'Appuyez sur Entrée pour ajouter <b>"' + String(value) + '"</b>';
|
||||
},
|
||||
maxItemText: (maxItemCount) => {
|
||||
return `${maxItemCount} valeurs peuvent être ajoutées`;
|
||||
maxItemText: function(maxItemCount) {
|
||||
return String(maxItemCount) + 'valeurs peuvent être ajoutées';
|
||||
},
|
||||
uniqueItemText: 'Cette valeur est déjà présente',
|
||||
});
|
||||
|
@ -297,7 +302,7 @@
|
|||
var textEmailFilter = new Choices('#choices-text-email-filter', {
|
||||
editItems: true,
|
||||
regexFilter: /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/,
|
||||
});
|
||||
}).setValue(['joe@bloggs.com']);
|
||||
|
||||
var textDisabled = new Choices('#choices-text-disabled', {
|
||||
addItems: false,
|
||||
|
@ -332,7 +337,30 @@
|
|||
|
||||
var multipleCancelButton = new Choices('#choices-multiple-remove-button', {
|
||||
removeItemButton: true,
|
||||
})
|
||||
});
|
||||
|
||||
/* Use label on event */
|
||||
var choicesSelect = new Choices('#choices-multiple-labels', {
|
||||
search: false,
|
||||
removeItemButton: true,
|
||||
choices: [
|
||||
{value: 'One', label: 'Label One'},
|
||||
{value: 'Two', label: 'Label Two', disabled: true},
|
||||
{value: 'Three', label: 'Label Three'},
|
||||
],
|
||||
}).setChoices([
|
||||
{value: 'Four', label: 'Label Four', disabled: true},
|
||||
{value: 'Five', label: 'Label Five'},
|
||||
{value: 'Six', label: 'Label Six', selected: true},
|
||||
], 'value', 'label', false);
|
||||
|
||||
choicesSelect.passedElement.addEventListener('addItem', function(event) {
|
||||
document.getElementById('message').innerHTML = 'You just added "' + event.detail.label + '"';
|
||||
});
|
||||
|
||||
choicesSelect.passedElement.addEventListener('removeItem', function(event) {
|
||||
document.getElementById('message').innerHTML = 'You just removed "' + event.detail.label + '"';
|
||||
});
|
||||
|
||||
var singleFetch = new Choices('#choices-single-remote-fetch', {
|
||||
searchPlaceholderValue: 'Pick an Arctic Monkeys record',
|
||||
|
@ -439,28 +467,42 @@
|
|||
}
|
||||
});
|
||||
|
||||
var singleCustomTemplates = new Choices(
|
||||
document.getElementById('choices-custom-templates'), {
|
||||
searchPlaceholderValue: 'Choose your favourite framework...',
|
||||
callbackOnCreateTemplates: function (strToEl) {
|
||||
var classNames = this.config.classNames;
|
||||
return {
|
||||
item: (data) => {
|
||||
return strToEl(`
|
||||
<div class="${classNames.item} ${data.highlighted ? classNames.highlightedState : classNames.itemSelectable}" data-item data-id="${data.id}" data-value="${data.value}" ${data.active ? 'aria-selected="true"' : ''} ${data.disabled ? 'aria-disabled="true"' : ''}>
|
||||
<span style="margin-right:10px;">🎉</span> ${data.label}
|
||||
</div>
|
||||
`);
|
||||
},
|
||||
choice: (data) => {
|
||||
return strToEl(`
|
||||
<div class="${classNames.item} ${classNames.itemChoice} ${data.disabled ? classNames.itemDisabled : classNames.itemSelectable}" data-select-text="${this.config.itemSelectText}" data-choice ${data.disabled ? 'data-choice-disabled aria-disabled="true"' : 'data-choice-selectable'} data-id="${data.id}" data-value="${data.value}" ${data.groupId > 0 ? 'role="treeitem"' : 'role="option"'}>
|
||||
<span style="margin-right:10px;">👉🏽</span> ${data.label}
|
||||
</div>
|
||||
`);
|
||||
},
|
||||
};
|
||||
}
|
||||
var customTemplates = new Choices(document.getElementById('choices-single-custom-templates'), {
|
||||
searchPlaceholderValue: 'Choose your favourite framework...',
|
||||
callbackOnCreateTemplates: function (strToEl) {
|
||||
var classNames = this.config.classNames;
|
||||
var itemSelectText = this.config.itemSelectText;
|
||||
return {
|
||||
item: function(data) {
|
||||
return strToEl('\
|
||||
<div\
|
||||
class="'+ String(classNames.item) + ' ' + String(data.highlighted ? classNames.highlightedState : classNames.itemSelectable) + '"\
|
||||
data-item\
|
||||
data-id="'+ String(data.id) + '"\
|
||||
data-value="'+ String(data.value) +'"\
|
||||
'+ String(data.active ? 'aria-selected="true"' : '') + '\
|
||||
'+ String(data.disabled ? 'aria-disabled="true"' : '') + '\
|
||||
>\
|
||||
<span style="margin-right:10px;">🎉</span> ' + String(data.label) + '\
|
||||
</div>\
|
||||
');
|
||||
},
|
||||
choice: function(data) {
|
||||
return strToEl('\
|
||||
<div\
|
||||
class="'+ String(classNames.item) + ' ' + String(classNames.itemChoice) + ' ' + String(data.disabled ? classNames.itemDisabled : classNames.itemSelectable) + '"\
|
||||
data-select-text="'+ String(itemSelectText) +'"\
|
||||
data-choice \
|
||||
'+ String(data.disabled ? 'data-choice-disabled aria-disabled="true"' : 'data-choice-selectable') + '\
|
||||
data-id="'+ String(data.id) +'"\
|
||||
data-value="'+ String(data.value) +'"\
|
||||
'+ String(data.groupId > 0 ? 'role="treeitem"' : 'role="option"') + '\
|
||||
>\
|
||||
<span style="margin-right:10px;">👉🏽</span> ' + String(data.label) + '\
|
||||
</div>\
|
||||
');
|
||||
},
|
||||
};
|
||||
}
|
||||
);
|
||||
});
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "choices.js",
|
||||
"version": "2.6.1",
|
||||
"version": "2.7.7",
|
||||
"description": "A vanilla JS customisable text input/select box plugin",
|
||||
"main": "./assets/scripts/dist/choices.min.js",
|
||||
"scripts": {
|
||||
|
@ -13,7 +13,8 @@
|
|||
"js:build": "concurrently --prefix-colors yellow,green \"webpack --minimize --config webpack.config.prod.js\" \"webpack --config webpack.config.prod.js\"",
|
||||
"js:test": "./node_modules/karma/bin/karma start --single-run --no-auto-watch tests/karma.config.js",
|
||||
"js:test:watch": "./node_modules/karma/bin/karma start --auto-watch --no-single-run tests/karma.config.js",
|
||||
"preversion": "npm run js:build"
|
||||
"version": "node version.js --current $npm_package_version --new $npm_config_newVersion",
|
||||
"postversion": "npm run js:build"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
|
|
@ -78,6 +78,7 @@ describe('Choices', () => {
|
|||
expect(this.choices.config.searchFloor).toEqual(jasmine.any(Number));
|
||||
expect(this.choices.config.searchPlaceholderValue).toEqual(null);
|
||||
expect(this.choices.config.flip).toEqual(jasmine.any(Boolean));
|
||||
expect(this.choices.config.position).toEqual(jasmine.any(String));
|
||||
expect(this.choices.config.regexFilter).toEqual(null);
|
||||
expect(this.choices.config.sortFilter).toEqual(jasmine.any(Function));
|
||||
expect(this.choices.config.sortFields).toEqual(jasmine.any(Array) || jasmine.any(String));
|
||||
|
@ -367,9 +368,56 @@ describe('Choices', () => {
|
|||
});
|
||||
|
||||
it('should close the dropdown on double click', function() {
|
||||
this.choices = new Choices(this.input);
|
||||
const container = this.choices.containerOuter,
|
||||
openState = this.choices.config.classNames.openState;
|
||||
|
||||
this.choices._onClick({
|
||||
target: container,
|
||||
ctrlKey: false,
|
||||
preventDefault: () => {}
|
||||
});
|
||||
|
||||
this.choices._onClick({
|
||||
target: container,
|
||||
ctrlKey: false,
|
||||
preventDefault: () => {}
|
||||
});
|
||||
|
||||
expect(document.activeElement === this.choices.input && container.classList.contains(openState)).toBe(false);
|
||||
});
|
||||
|
||||
it('should trigger showDropdown on dropdown opening', function() {
|
||||
this.choices = new Choices(this.input);
|
||||
const container = this.choices.containerOuter;
|
||||
|
||||
const showDropdownSpy = jasmine.createSpy('showDropdownSpy');
|
||||
const passedElement = this.choices.passedElement;
|
||||
|
||||
passedElement.addEventListener('showDropdown', showDropdownSpy);
|
||||
|
||||
this.choices.input.focus();
|
||||
|
||||
this.choices._onClick({
|
||||
target: container,
|
||||
ctrlKey: false,
|
||||
preventDefault: () => {}
|
||||
});
|
||||
|
||||
expect(showDropdownSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should trigger hideDropdown on dropdown closing', function() {
|
||||
this.choices = new Choices(this.input);
|
||||
const container = this.choices.containerOuter;
|
||||
|
||||
const hideDropdownSpy = jasmine.createSpy('hideDropdownSpy');
|
||||
const passedElement = this.choices.passedElement;
|
||||
|
||||
passedElement.addEventListener('hideDropdown', hideDropdownSpy);
|
||||
|
||||
this.choices.input.focus();
|
||||
|
||||
this.choices._onClick({
|
||||
target: container,
|
||||
ctrlKey: false,
|
||||
|
@ -382,7 +430,7 @@ describe('Choices', () => {
|
|||
preventDefault: () => {}
|
||||
});
|
||||
|
||||
expect(document.activeElement === this.choices.input && container.classList.contains('is-open')).toBe(false);
|
||||
expect(hideDropdownSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should filter choices when searching', function() {
|
||||
|
@ -735,6 +783,42 @@ describe('Choices', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('should handle public methods on select-one input types', function() {
|
||||
beforeEach(function() {
|
||||
this.input = document.createElement('select');
|
||||
this.input.className = 'js-choices';
|
||||
this.input.placeholder = 'Placeholder text';
|
||||
|
||||
for (let i = 1; i < 10; i++) {
|
||||
const option = document.createElement('option');
|
||||
|
||||
option.value = `Value ${i}`;
|
||||
option.innerHTML = `Value ${i}`;
|
||||
|
||||
if (i % 2) {
|
||||
option.selected = true;
|
||||
}
|
||||
|
||||
this.input.appendChild(option);
|
||||
}
|
||||
|
||||
document.body.appendChild(this.input);
|
||||
this.choices = new Choices(this.input);
|
||||
});
|
||||
|
||||
it('should handle disable()', function() {
|
||||
this.choices.disable();
|
||||
|
||||
expect(this.choices.containerOuter.getAttribute('tabindex')).toBe('-1');
|
||||
});
|
||||
|
||||
it('should handle enable()', function() {
|
||||
this.choices.enable();
|
||||
|
||||
expect(this.choices.containerOuter.getAttribute('tabindex')).toBe('0');
|
||||
});
|
||||
});
|
||||
|
||||
describe('should handle public methods on text input types', function() {
|
||||
beforeEach(function() {
|
||||
this.input = document.createElement('input');
|
||||
|
@ -759,4 +843,47 @@ describe('Choices', () => {
|
|||
expect(randomItem.active).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('should react to config options', function() {
|
||||
beforeEach(function() {
|
||||
this.input = document.createElement('select');
|
||||
this.input.className = 'js-choices';
|
||||
this.input.setAttribute('multiple', '');
|
||||
|
||||
for (let i = 1; i < 4; i++) {
|
||||
const option = document.createElement('option');
|
||||
|
||||
option.value = `Value ${i}`;
|
||||
option.innerHTML = `Value ${i}`;
|
||||
|
||||
if (i % 2) {
|
||||
option.selected = true;
|
||||
}
|
||||
|
||||
this.input.appendChild(option);
|
||||
}
|
||||
|
||||
document.body.appendChild(this.input);
|
||||
});
|
||||
|
||||
it('should flip the dropdown', function() {
|
||||
this.choices = new Choices(this.input, {
|
||||
position: 'top'
|
||||
});
|
||||
|
||||
const container = this.choices.containerOuter;
|
||||
this.choices.input.focus();
|
||||
expect(container.classList.contains(this.choices.config.classNames.flippedState)).toBe(true);
|
||||
});
|
||||
|
||||
it('shouldn\'t flip the dropdown', function() {
|
||||
this.choices = new Choices(this.input, {
|
||||
position: 'bottom'
|
||||
});
|
||||
|
||||
const container = this.choices.containerOuter;
|
||||
this.choices.input.focus();
|
||||
expect(container.classList.contains(this.choices.config.classNames.flippedState)).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
50
version.js
Normal file
50
version.js
Normal file
|
@ -0,0 +1,50 @@
|
|||
// Example usage: npm --newVersion=2.7.2 run version
|
||||
|
||||
const fs = require('fs'),
|
||||
path = require('path'),
|
||||
config = {
|
||||
files: ['bower.json', 'package.json', 'index.html']
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert node arguments into an object
|
||||
* @return {Object} Arguments
|
||||
*/
|
||||
const argvToObject = () => {
|
||||
const args = {};
|
||||
let arg = null;
|
||||
process.argv.forEach((val, index) => {
|
||||
if(/^--/.test(val)) {
|
||||
arg = {
|
||||
index: index,
|
||||
name: val.replace(/^--/, '')
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if(arg && ((arg.index+1 === index ))) {
|
||||
args[arg.name] = val;
|
||||
}
|
||||
});
|
||||
|
||||
return args;
|
||||
};
|
||||
|
||||
const updateVersion = (config) => {
|
||||
const args = argvToObject();
|
||||
const currentVersion = args.current;
|
||||
const newVersion = args.new;
|
||||
console.log(`Updating version from ${currentVersion} to ${newVersion}`);
|
||||
config.files.forEach((file) => {
|
||||
const filePath = path.join(__dirname, file);
|
||||
const regex = new RegExp(currentVersion, 'g');
|
||||
|
||||
let contents = fs.readFileSync(filePath, 'utf-8');
|
||||
contents = contents.replace(regex, newVersion);
|
||||
fs.writeFileSync(filePath, contents);
|
||||
});
|
||||
|
||||
console.log(`Updated version to ${newVersion}`);
|
||||
};
|
||||
|
||||
updateVersion(config);
|
Loading…
Reference in a new issue