mirror of
https://github.com/Choices-js/Choices.git
synced 2024-05-20 06:26:36 +02:00
Merge branch 'JayKid-master'
This commit is contained in:
commit
2d5c863f5a
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -2,7 +2,7 @@ node_modules
|
|||
npm-debug.log
|
||||
.DS_Store
|
||||
.vscode
|
||||
.package-lock.json
|
||||
package-lock.json
|
||||
|
||||
# Test
|
||||
tests/reports
|
||||
|
|
23
README.md
23
README.md
|
@ -165,7 +165,10 @@ Pass an array of objects:
|
|||
{
|
||||
value: 'Value 2',
|
||||
label: 'Label 2',
|
||||
id: 2
|
||||
id: 2,
|
||||
customProperties: {
|
||||
random: 'I am a custom property'
|
||||
}
|
||||
}]
|
||||
```
|
||||
|
||||
|
@ -190,6 +193,10 @@ Pass an array of objects:
|
|||
label: 'Option 2',
|
||||
selected: false,
|
||||
disabled: true,
|
||||
customProperties: {
|
||||
description: 'Custom description about Option 2',
|
||||
random: 'Another random custom property'
|
||||
},
|
||||
}]
|
||||
```
|
||||
|
||||
|
@ -269,7 +276,7 @@ Pass an array of objects:
|
|||
|
||||
**Input types affected:**`select-one`, `select-multiple`
|
||||
|
||||
**Usage:** Specify which fields should be used when a user is searching.
|
||||
**Usage:** Specify which fields should be used when a user is searching. If you have added custom properties to your choices, you can add these values thus: `['label', 'value', 'customProperties.example']`.
|
||||
|
||||
### searchFloor
|
||||
**Type:** `Number` **Default:** `1`
|
||||
|
@ -506,7 +513,6 @@ example.passedElement.addEventListener('addItem', function(event) {
|
|||
console.log(event.detail.label);
|
||||
console.log(event.detail.groupValue);
|
||||
}, false);
|
||||
|
||||
```
|
||||
|
||||
### addItem
|
||||
|
@ -574,7 +580,9 @@ Methods can be called either directly or by chaining:
|
|||
const choices = new Choices(element, {
|
||||
addItems: false,
|
||||
removeItems: false,
|
||||
}).setValue(['Set value 1', 'Set value 2']).disable();
|
||||
})
|
||||
.setValue(['Set value 1', 'Set value 2'])
|
||||
.disable();
|
||||
|
||||
// Calling a method directly
|
||||
const choices = new Choices(element, {
|
||||
|
@ -648,7 +656,7 @@ choices.disable();
|
|||
### setChoices(choices, value, label, replaceChoices);
|
||||
**Input types affected:** `select-one`, `select-multiple`
|
||||
|
||||
**Usage:** Set choices of select input via an array of objects, a value name and a label name. This behaves the same as passing items via the `choices` option but can be called after initialising Choices. This can also be used to add groups of choices (see example 2); Optionally pass a true `replaceChoices` value to remove any existing choices.
|
||||
**Usage:** Set choices of select input via an array of objects, a value name and a label name. This behaves the same as passing items via the `choices` option but can be called after initialising Choices. This can also be used to add groups of choices (see example 2); Optionally pass a true `replaceChoices` value to remove any existing choices. Optionally pass a `customProperties` object to add additional data to your choices (useful when searching/filtering etc).
|
||||
|
||||
**Example 1:**
|
||||
|
||||
|
@ -684,7 +692,10 @@ example.setChoices([{
|
|||
choices: [
|
||||
{value: 'Child Four', label: 'Child Four', disabled: true},
|
||||
{value: 'Child Five', label: 'Child Five'},
|
||||
{value: 'Child Six', label: 'Child Six'},
|
||||
{value: 'Child Six', label: 'Child Six', customProperties: {
|
||||
description: 'Custom description about child six',
|
||||
random: 'Another random custom property'
|
||||
}},
|
||||
]
|
||||
}], 'value', 'label', false);
|
||||
```
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
export const addItem = (value, label, id, choiceId, groupId) => {
|
||||
export const addItem = (value, label, id, choiceId, groupId, customProperties) => {
|
||||
return {
|
||||
type: 'ADD_ITEM',
|
||||
value,
|
||||
|
@ -6,6 +6,7 @@ export const addItem = (value, label, id, choiceId, groupId) => {
|
|||
id,
|
||||
choiceId,
|
||||
groupId,
|
||||
customProperties
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -25,7 +26,7 @@ export const highlightItem = (id, highlighted) => {
|
|||
};
|
||||
};
|
||||
|
||||
export const addChoice = (value, label, id, groupId, disabled, elementId) => {
|
||||
export const addChoice = (value, label, id, groupId, disabled, elementId, customProperties) => {
|
||||
return {
|
||||
type: 'ADD_CHOICE',
|
||||
value,
|
||||
|
@ -34,6 +35,7 @@ export const addChoice = (value, label, id, groupId, disabled, elementId) => {
|
|||
groupId,
|
||||
disabled,
|
||||
elementId: elementId,
|
||||
customProperties
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -812,13 +812,16 @@ class Choices {
|
|||
item.label,
|
||||
true,
|
||||
false,
|
||||
-1
|
||||
-1,
|
||||
item.customProperties
|
||||
);
|
||||
} else {
|
||||
this._addItem(
|
||||
item.value,
|
||||
item.label,
|
||||
item.id
|
||||
item.id,
|
||||
undefined,
|
||||
item.customProperties
|
||||
);
|
||||
}
|
||||
} else if (itemType === 'String') {
|
||||
|
@ -868,7 +871,13 @@ class Choices {
|
|||
|
||||
if (foundChoice) {
|
||||
if (!foundChoice.selected) {
|
||||
this._addItem(foundChoice.value, foundChoice.label, foundChoice.id, foundChoice.groupId);
|
||||
this._addItem(
|
||||
foundChoice.value,
|
||||
foundChoice.label,
|
||||
foundChoice.id,
|
||||
foundChoice.groupId,
|
||||
foundChoice.customProperties
|
||||
);
|
||||
} else if (!this.config.silent) {
|
||||
console.warn('Attempting to select choice already selected');
|
||||
}
|
||||
|
@ -915,7 +924,9 @@ class Choices {
|
|||
result[value],
|
||||
result[label],
|
||||
result.selected,
|
||||
result.disabled
|
||||
result.disabled,
|
||||
undefined,
|
||||
result['customProperties']
|
||||
);
|
||||
}
|
||||
});
|
||||
|
@ -1134,7 +1145,8 @@ class Choices {
|
|||
choice.value,
|
||||
choice.label,
|
||||
choice.id,
|
||||
choice.groupId
|
||||
choice.groupId,
|
||||
choice.customProperties
|
||||
);
|
||||
this._triggerChange(choice.value);
|
||||
}
|
||||
|
@ -1305,7 +1317,9 @@ class Choices {
|
|||
result[value],
|
||||
result[label],
|
||||
result.selected,
|
||||
result.disabled
|
||||
result.disabled,
|
||||
undefined,
|
||||
result['customProperties']
|
||||
);
|
||||
}
|
||||
});
|
||||
|
@ -2058,10 +2072,13 @@ class Choices {
|
|||
* Add item to store with correct value
|
||||
* @param {String} value Value to add to store
|
||||
* @param {String} label Label to add to store
|
||||
* @param {Number} choiceId ID of the associated choice that was selected
|
||||
* @param {Number} groupId ID of group choice is within. Negative number indicates no group
|
||||
* @param {Object} customProperties Object containing user defined properties
|
||||
* @return {Object} Class instance
|
||||
* @public
|
||||
*/
|
||||
_addItem(value, label, choiceId = -1, groupId = -1) {
|
||||
_addItem(value, label, choiceId = -1, groupId = -1, customProperties = null) {
|
||||
let passedValue = isType('String', value) ? value.trim() : value;
|
||||
const items = this.store.getItems();
|
||||
const passedLabel = label || passedValue;
|
||||
|
@ -2083,7 +2100,7 @@ class Choices {
|
|||
passedValue += this.config.appendValue.toString();
|
||||
}
|
||||
|
||||
this.store.dispatch(addItem(passedValue, passedLabel, id, passedOptionId, groupId));
|
||||
this.store.dispatch(addItem(passedValue, passedLabel, id, passedOptionId, groupId, customProperties));
|
||||
|
||||
if (this.passedElement.type === 'select-one') {
|
||||
this.removeActiveItems(id);
|
||||
|
@ -2152,15 +2169,16 @@ class Choices {
|
|||
|
||||
/**
|
||||
* Add choice to dropdown
|
||||
* @param {Boolean} isSelected Whether choice is selected
|
||||
* @param {Boolean} isDisabled Whether choice is disabled
|
||||
* @param {String} value Value of choice
|
||||
* @param {String} Label Label of choice
|
||||
* @param {Boolean} isSelected Whether choice is selected
|
||||
* @param {Boolean} isDisabled Whether choice is disabled
|
||||
* @param {Number} groupId ID of group choice is within. Negative number indicates no group
|
||||
* @param {Object} customProperties Object containing user defined properties
|
||||
* @return
|
||||
* @private
|
||||
*/
|
||||
_addChoice(value, label, isSelected = false, isDisabled = false, groupId = -1) {
|
||||
_addChoice(value, label, isSelected = false, isDisabled = false, groupId = -1, customProperties = null) {
|
||||
if (typeof value === 'undefined' || value === null) {
|
||||
return;
|
||||
}
|
||||
|
@ -2177,11 +2195,19 @@ class Choices {
|
|||
choiceId,
|
||||
groupId,
|
||||
isDisabled,
|
||||
choiceElementId
|
||||
choiceElementId,
|
||||
customProperties
|
||||
));
|
||||
|
||||
if (isSelected) {
|
||||
this._addItem(value, choiceLabel, choiceId);
|
||||
this._addItem(
|
||||
value,
|
||||
choiceLabel,
|
||||
choiceId,
|
||||
undefined,
|
||||
undefined,
|
||||
customProperties
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2349,7 +2375,7 @@ class Choices {
|
|||
type="button"
|
||||
class="${globalClasses.button}"
|
||||
data-button
|
||||
aria-label="Remove item '${data.value}'"
|
||||
aria-label="Remove item: '${data.value}'"
|
||||
>
|
||||
Remove item
|
||||
</button>
|
||||
|
@ -2618,7 +2644,9 @@ class Choices {
|
|||
choice.value,
|
||||
choice.label,
|
||||
choice.selected,
|
||||
choice.disabled
|
||||
choice.disabled,
|
||||
undefined,
|
||||
choice.customProperties
|
||||
);
|
||||
} else {
|
||||
// Otherwise pre-select the first choice in the array
|
||||
|
@ -2626,7 +2654,9 @@ class Choices {
|
|||
choice.value,
|
||||
choice.label,
|
||||
true,
|
||||
false
|
||||
false,
|
||||
undefined,
|
||||
choice.customProperties
|
||||
);
|
||||
}
|
||||
} else {
|
||||
|
@ -2634,7 +2664,9 @@ class Choices {
|
|||
choice.value,
|
||||
choice.label,
|
||||
choice.selected,
|
||||
choice.disabled
|
||||
choice.disabled,
|
||||
undefined,
|
||||
choice.customProperties
|
||||
);
|
||||
}
|
||||
});
|
||||
|
@ -2647,7 +2679,13 @@ class Choices {
|
|||
if (!item.value) {
|
||||
return;
|
||||
}
|
||||
this._addItem(item.value, item.label, item.id);
|
||||
this._addItem(
|
||||
item.value,
|
||||
item.label,
|
||||
item.id,
|
||||
undefined,
|
||||
item.customProperties
|
||||
);
|
||||
} else if (itemType === 'String') {
|
||||
this._addItem(item);
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@ const choices = (state = [], action) => {
|
|||
selected: false,
|
||||
active: true,
|
||||
score: 9999,
|
||||
customProperties: action.customProperties
|
||||
}];
|
||||
}
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ const items = (state = [], action) => {
|
|||
label: action.label,
|
||||
active: true,
|
||||
highlighted: false,
|
||||
customProperties: action.customProperties
|
||||
}];
|
||||
|
||||
return newState.map((item) => {
|
||||
|
|
18
index.html
18
index.html
|
@ -63,7 +63,7 @@
|
|||
<input class="form-control" id="choices-text-prepend-append-value" type="text" value="preset-1, preset-2" placeholder="This is a placeholder">
|
||||
|
||||
<label for="choices-text-preset-values">Preset values passed through options</label>
|
||||
<input class="form-control" id="choices-text-preset-values" type="text" value="olivia@benson.com" placeholder="This is a placeholder">
|
||||
<input class="form-control" id="choices-text-preset-values" type="text" value="Michael Smith" placeholder="This is a placeholder">
|
||||
|
||||
<label for="choices-text-i18n">I18N labels</label>
|
||||
<input class="form-control" data-trigger id="choices-text-i18n" type="text">
|
||||
|
@ -208,7 +208,8 @@
|
|||
<label for="choices-single-preset-options">Option and option groups added via config</label>
|
||||
<select class="form-control" name="choices-single-preset-options" id="choices-single-preset-options" placeholder="This is a placeholder"></select>
|
||||
|
||||
<label for="choices-single-selected-option">Option selected via config</label>
|
||||
<label for="choices-single-selected-option">Option selected via config with custom properties</label>
|
||||
<p><small>Try searching for 'fantastic'</small></p>
|
||||
<select class="form-control" name="choices-single-selected-option" id="choices-single-selected-option" placeholder="This is a placeholder"></select>
|
||||
|
||||
<label for="choices-single-no-sorting">Options without sorting</label>
|
||||
|
@ -306,7 +307,13 @@
|
|||
}).removeActiveItems();
|
||||
|
||||
var textPresetVal = new Choices('#choices-text-preset-values', {
|
||||
items: ['josh@joshuajohnson.co.uk', { value: 'joe@bloggs.co.uk', label: 'Joe Bloggs' } ],
|
||||
items: ['Josh Johnson', {
|
||||
value: 'joe@bloggs.co.uk',
|
||||
label: 'Joe Bloggs',
|
||||
customProperties: {
|
||||
description: 'Joe Blogg is such a generic name'
|
||||
}
|
||||
}],
|
||||
});
|
||||
|
||||
var multipleDefault = new Choices(document.getElementById('choices-multiple-groups'));
|
||||
|
@ -433,10 +440,13 @@
|
|||
}], 'value', 'label');
|
||||
|
||||
var singleSelectedOpt = new Choices('#choices-single-selected-option', {
|
||||
searchFields: ['label', 'value', 'customProperties.description'],
|
||||
choices: [
|
||||
{value: 'One', label: 'Label One', selected: true},
|
||||
{value: 'Two', label: 'Label Two', disabled: true},
|
||||
{value: 'Three', label: 'Label Three'},
|
||||
{value: 'Three', label: 'Label Three', customProperties: {
|
||||
description: 'This option is fantastic'
|
||||
}},
|
||||
],
|
||||
}).setValueByChoice('Two');
|
||||
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
"babel-loader": "^6.2.4",
|
||||
"babel-preset-es2015": "^6.6.0",
|
||||
"concurrently": "^3.1.0",
|
||||
"core-js": "^2.4.1",
|
||||
"csso": "^1.8.2",
|
||||
"es6-promise": "^3.2.1",
|
||||
"eslint": "^3.3.0",
|
||||
|
|
|
@ -1,39 +1,16 @@
|
|||
import 'whatwg-fetch';
|
||||
import 'es6-promise';
|
||||
import 'core-js/fn/object/assign';
|
||||
import Choices from '../../assets/scripts/src/choices.js';
|
||||
|
||||
if (typeof Object.assign != 'function') {
|
||||
Object.assign = function (target, varArgs) { // .length of function is 2
|
||||
if (target == null) { // TypeError if undefined or null
|
||||
throw new TypeError('Cannot convert undefined or null to object');
|
||||
}
|
||||
|
||||
var to = Object(target);
|
||||
|
||||
for (var index = 1; index < arguments.length; index++) {
|
||||
var nextSource = arguments[index];
|
||||
|
||||
if (nextSource != null) { // Skip over if undefined or null
|
||||
for (var nextKey in nextSource) {
|
||||
// Avoid bugs when hasOwnProperty is shadowed
|
||||
if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {
|
||||
to[nextKey] = nextSource[nextKey];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return to;
|
||||
};
|
||||
}
|
||||
import itemReducer from '../../assets/scripts/src/reducers/items.js';
|
||||
import choiceReducer from '../../assets/scripts/src/reducers/choices.js';
|
||||
import {
|
||||
addItem as addItemAction,
|
||||
addChoice as addChoiceAction
|
||||
} from '../../assets/scripts/src/actions/index.js';
|
||||
|
||||
describe('Choices', () => {
|
||||
|
||||
afterEach(function() {
|
||||
this.choices.destroy();
|
||||
});
|
||||
|
||||
describe('should initialize Choices', () => {
|
||||
|
||||
beforeEach(function() {
|
||||
this.input = document.createElement('input');
|
||||
this.input.type = 'text';
|
||||
|
@ -43,6 +20,10 @@ describe('Choices', () => {
|
|||
this.choices = new Choices(this.input);
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
this.choices.destroy();
|
||||
});
|
||||
|
||||
it('should be defined', function() {
|
||||
expect(this.choices).toBeDefined();
|
||||
});
|
||||
|
@ -164,6 +145,10 @@ describe('Choices', () => {
|
|||
document.body.appendChild(this.input);
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
this.choices.destroy();
|
||||
});
|
||||
|
||||
it('should accept a user inputted value', function() {
|
||||
this.choices = new Choices(this.input);
|
||||
|
||||
|
@ -272,6 +257,10 @@ describe('Choices', () => {
|
|||
document.body.appendChild(this.input);
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
this.choices.destroy();
|
||||
});
|
||||
|
||||
it('should open the choice list on focussing', function() {
|
||||
this.choices = new Choices(this.input);
|
||||
this.choices.input.focus();
|
||||
|
@ -567,6 +556,10 @@ describe('Choices', () => {
|
|||
});
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
this.choices.destroy();
|
||||
});
|
||||
|
||||
it('should add any pre-defined values', function() {
|
||||
expect(this.choices.currentState.items.length).toBeGreaterThan(1);
|
||||
});
|
||||
|
@ -604,6 +597,10 @@ describe('Choices', () => {
|
|||
this.choices = new Choices(this.input);
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
this.choices.destroy();
|
||||
});
|
||||
|
||||
it('should handle highlightItem()', function() {
|
||||
const items = this.choices.currentState.items;
|
||||
const randomItem = items[Math.floor(Math.random() * items.length)];
|
||||
|
@ -839,6 +836,10 @@ describe('Choices', () => {
|
|||
this.choices = new Choices(this.input);
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
this.choices.destroy();
|
||||
});
|
||||
|
||||
it('should handle disable()', function() {
|
||||
this.choices.disable();
|
||||
|
||||
|
@ -863,6 +864,10 @@ describe('Choices', () => {
|
|||
this.choices = new Choices(this.input);
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
this.choices.destroy();
|
||||
});
|
||||
|
||||
it('should handle clearInput()', function() {
|
||||
this.choices.clearInput();
|
||||
expect(this.choices.input.value).toBe('');
|
||||
|
@ -899,6 +904,10 @@ describe('Choices', () => {
|
|||
document.body.appendChild(this.input);
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
this.choices.destroy();
|
||||
});
|
||||
|
||||
it('should flip the dropdown', function() {
|
||||
this.choices = new Choices(this.input, {
|
||||
position: 'top'
|
||||
|
@ -919,4 +928,115 @@ describe('Choices', () => {
|
|||
expect(container.classList.contains(this.choices.config.classNames.flippedState)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('should allow custom properties provided by the user on items or choices', function() {
|
||||
it('should allow the user to supply custom properties for an item', function() {
|
||||
const randomItem = {
|
||||
id: 8999,
|
||||
choiceId: 9000,
|
||||
groupId: 9001,
|
||||
value: 'value',
|
||||
label: 'label',
|
||||
customProperties: {
|
||||
foo: 'bar'
|
||||
}
|
||||
}
|
||||
|
||||
const expectedState = [{
|
||||
id: randomItem.id,
|
||||
choiceId: randomItem.choiceId,
|
||||
groupId: randomItem.groupId,
|
||||
value: randomItem.value,
|
||||
label: randomItem.label,
|
||||
active: true,
|
||||
highlighted: false,
|
||||
customProperties: randomItem.customProperties
|
||||
}];
|
||||
|
||||
const action = addItemAction(
|
||||
randomItem.value,
|
||||
randomItem.label,
|
||||
randomItem.id,
|
||||
randomItem.choiceId,
|
||||
randomItem.groupId,
|
||||
randomItem.customProperties
|
||||
);
|
||||
|
||||
expect(itemReducer([], action)).toEqual(expectedState);
|
||||
});
|
||||
|
||||
it('should allow the user to supply custom properties for a choice', function() {
|
||||
const randomChoice = {
|
||||
id: 123,
|
||||
elementId: 321,
|
||||
groupId: 213,
|
||||
value: 'value',
|
||||
label: 'label',
|
||||
disabled: false,
|
||||
customProperties: {
|
||||
foo: 'bar'
|
||||
}
|
||||
}
|
||||
|
||||
const expectedState = [{
|
||||
id: randomChoice.id,
|
||||
elementId: randomChoice.elementId,
|
||||
groupId: randomChoice.groupId,
|
||||
value: randomChoice.value,
|
||||
label: randomChoice.label,
|
||||
disabled: randomChoice.disabled,
|
||||
selected: false,
|
||||
active: true,
|
||||
score: 9999,
|
||||
customProperties: randomChoice.customProperties
|
||||
}];
|
||||
|
||||
const action = addChoiceAction(
|
||||
randomChoice.value,
|
||||
randomChoice.label,
|
||||
randomChoice.id,
|
||||
randomChoice.groupId,
|
||||
randomChoice.disabled,
|
||||
randomChoice.elementId,
|
||||
randomChoice.customProperties
|
||||
);
|
||||
|
||||
expect(choiceReducer([], action)).toEqual(expectedState);
|
||||
});
|
||||
});
|
||||
|
||||
describe('should allow custom properties provided by the user on items or choices', function() {
|
||||
beforeEach(function() {
|
||||
this.input = document.createElement('select');
|
||||
this.input.className = 'js-choices';
|
||||
this.input.setAttribute('multiple', '');
|
||||
|
||||
document.body.appendChild(this.input);
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
this.choices.destroy();
|
||||
});
|
||||
|
||||
it('should allow the user to supply custom properties for a choice that will be inherited by the item when the user selects the choice', function() {
|
||||
const expectedCustomProperties = {
|
||||
isBestOptionEver: true
|
||||
};
|
||||
|
||||
this.choices = new Choices(this.input);
|
||||
this.choices.setChoices([{
|
||||
value: '42',
|
||||
label: 'My awesome choice',
|
||||
selected: false,
|
||||
disabled: false,
|
||||
customProperties: expectedCustomProperties
|
||||
}], 'value', 'label', true);
|
||||
|
||||
this.choices.setValueByChoice('42');
|
||||
const selectedItems = this.choices.getValue();
|
||||
|
||||
expect(selectedItems.length).toBe(1);
|
||||
expect(selectedItems[0].customProperties).toBe(expectedCustomProperties);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue