feat: implement custom properties for items and choices

+ Added unit tests for both choice and item reducers
+ Had to add some visually unpleasant undefineds, sorry :P
This commit is contained in:
Jay Kid 2017-06-28 11:11:02 +02:00
parent 1eaceb1345
commit 0b3cf1a355
5 changed files with 145 additions and 22 deletions

View file

@ -1,4 +1,4 @@
export const addItem = (value, label, id, choiceId, groupId) => { export const addItem = (value, label, id, choiceId, groupId, customProperties) => {
return { return {
type: 'ADD_ITEM', type: 'ADD_ITEM',
value, value,
@ -6,6 +6,7 @@ export const addItem = (value, label, id, choiceId, groupId) => {
id, id,
choiceId, choiceId,
groupId, 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 { return {
type: 'ADD_CHOICE', type: 'ADD_CHOICE',
value, value,
@ -34,6 +35,7 @@ export const addChoice = (value, label, id, groupId, disabled, elementId) => {
groupId, groupId,
disabled, disabled,
elementId: elementId, elementId: elementId,
customProperties
}; };
}; };

View file

@ -812,13 +812,16 @@ class Choices {
item.label, item.label,
true, true,
false, false,
-1 -1,
item.customProperties
); );
} else { } else {
this._addItem( this._addItem(
item.value, item.value,
item.label, item.label,
item.id item.id,
undefined,
item.customProperties
); );
} }
} else if (itemType === 'String') { } else if (itemType === 'String') {
@ -868,7 +871,7 @@ class Choices {
if (foundChoice) { if (foundChoice) {
if (!foundChoice.selected) { 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) { } else if (!this.config.silent) {
console.warn('Attempting to select choice already selected'); console.warn('Attempting to select choice already selected');
} }
@ -915,7 +918,9 @@ class Choices {
result[value], result[value],
result[label], result[label],
result.selected, result.selected,
result.disabled result.disabled,
undefined,
result['customProperties']
); );
} }
}); });
@ -1134,7 +1139,8 @@ class Choices {
choice.value, choice.value,
choice.label, choice.label,
choice.id, choice.id,
choice.groupId choice.groupId,
choice.customProperties
); );
this._triggerChange(choice.value); this._triggerChange(choice.value);
} }
@ -1305,7 +1311,9 @@ class Choices {
result[value], result[value],
result[label], result[label],
result.selected, result.selected,
result.disabled result.disabled,
undefined,
result['customProperties']
); );
} }
}); });
@ -2058,10 +2066,13 @@ class Choices {
* Add item to store with correct value * Add item to store with correct value
* @param {String} value Value to add to store * @param {String} value Value to add to store
* @param {String} label Label 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 * @return {Object} Class instance
* @public * @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; let passedValue = isType('String', value) ? value.trim() : value;
const items = this.store.getItems(); const items = this.store.getItems();
const passedLabel = label || passedValue; const passedLabel = label || passedValue;
@ -2083,7 +2094,7 @@ class Choices {
passedValue += this.config.appendValue.toString(); 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') { if (this.passedElement.type === 'select-one') {
this.removeActiveItems(id); this.removeActiveItems(id);
@ -2152,15 +2163,16 @@ class Choices {
/** /**
* Add choice to dropdown * 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} value Value of choice
* @param {String} Label Label 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 {Number} groupId ID of group choice is within. Negative number indicates no group
* @param {Object} customProperties Object containing user defined properties
* @return * @return
* @private * @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) { if (typeof value === 'undefined' || value === null) {
return; return;
} }
@ -2177,11 +2189,19 @@ class Choices {
choiceId, choiceId,
groupId, groupId,
isDisabled, isDisabled,
choiceElementId choiceElementId,
customProperties
)); ));
if (isSelected) { if (isSelected) {
this._addItem(value, choiceLabel, choiceId); this._addItem(
value,
choiceLabel,
choiceId,
undefined,
undefined,
customProperties
);
} }
} }
@ -2618,7 +2638,9 @@ class Choices {
choice.value, choice.value,
choice.label, choice.label,
choice.selected, choice.selected,
choice.disabled choice.disabled,
undefined,
choice.customProperties
); );
} else { } else {
// Otherwise pre-select the first choice in the array // Otherwise pre-select the first choice in the array
@ -2626,7 +2648,9 @@ class Choices {
choice.value, choice.value,
choice.label, choice.label,
true, true,
false false,
undefined,
choice.customProperties
); );
} }
} else { } else {
@ -2634,7 +2658,9 @@ class Choices {
choice.value, choice.value,
choice.label, choice.label,
choice.selected, choice.selected,
choice.disabled choice.disabled,
undefined,
choice.customProperties
); );
} }
}); });

View file

@ -16,6 +16,7 @@ const choices = (state = [], action) => {
selected: false, selected: false,
active: true, active: true,
score: 9999, score: 9999,
customProperties: action.customProperties
}]; }];
} }

View file

@ -10,6 +10,7 @@ const items = (state = [], action) => {
label: action.label, label: action.label,
active: true, active: true,
highlighted: false, highlighted: false,
customProperties: action.customProperties
}]; }];
return newState.map((item) => { return newState.map((item) => {

View file

@ -1,6 +1,9 @@
import 'whatwg-fetch'; import 'whatwg-fetch';
import 'es6-promise'; import 'es6-promise';
import Choices from '../../assets/scripts/src/choices.js'; import Choices from '../../assets/scripts/src/choices.js';
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';
if (typeof Object.assign != 'function') { if (typeof Object.assign != 'function') {
Object.assign = function (target, varArgs) { // .length of function is 2 Object.assign = function (target, varArgs) { // .length of function is 2
@ -28,10 +31,6 @@ if (typeof Object.assign != 'function') {
describe('Choices', () => { describe('Choices', () => {
afterEach(function() {
this.choices.destroy();
});
describe('should initialize Choices', () => { describe('should initialize Choices', () => {
beforeEach(function() { beforeEach(function() {
@ -43,6 +42,10 @@ describe('Choices', () => {
this.choices = new Choices(this.input); this.choices = new Choices(this.input);
}); });
afterEach(function() {
this.choices.destroy();
});
it('should be defined', function() { it('should be defined', function() {
expect(this.choices).toBeDefined(); expect(this.choices).toBeDefined();
}); });
@ -164,6 +167,10 @@ describe('Choices', () => {
document.body.appendChild(this.input); document.body.appendChild(this.input);
}); });
afterEach(function() {
this.choices.destroy();
});
it('should accept a user inputted value', function() { it('should accept a user inputted value', function() {
this.choices = new Choices(this.input); this.choices = new Choices(this.input);
@ -272,6 +279,10 @@ describe('Choices', () => {
document.body.appendChild(this.input); document.body.appendChild(this.input);
}); });
afterEach(function() {
this.choices.destroy();
});
it('should open the choice list on focussing', function() { it('should open the choice list on focussing', function() {
this.choices = new Choices(this.input); this.choices = new Choices(this.input);
this.choices.input.focus(); this.choices.input.focus();
@ -567,6 +578,10 @@ describe('Choices', () => {
}); });
}); });
afterEach(function() {
this.choices.destroy();
});
it('should add any pre-defined values', function() { it('should add any pre-defined values', function() {
expect(this.choices.currentState.items.length).toBeGreaterThan(1); expect(this.choices.currentState.items.length).toBeGreaterThan(1);
}); });
@ -604,6 +619,10 @@ describe('Choices', () => {
this.choices = new Choices(this.input); this.choices = new Choices(this.input);
}); });
afterEach(function() {
this.choices.destroy();
});
it('should handle highlightItem()', function() { it('should handle highlightItem()', function() {
const items = this.choices.currentState.items; const items = this.choices.currentState.items;
const randomItem = items[Math.floor(Math.random() * items.length)]; const randomItem = items[Math.floor(Math.random() * items.length)];
@ -839,6 +858,10 @@ describe('Choices', () => {
this.choices = new Choices(this.input); this.choices = new Choices(this.input);
}); });
afterEach(function() {
this.choices.destroy();
});
it('should handle disable()', function() { it('should handle disable()', function() {
this.choices.disable(); this.choices.disable();
@ -863,6 +886,10 @@ describe('Choices', () => {
this.choices = new Choices(this.input); this.choices = new Choices(this.input);
}); });
afterEach(function() {
this.choices.destroy();
});
it('should handle clearInput()', function() { it('should handle clearInput()', function() {
this.choices.clearInput(); this.choices.clearInput();
expect(this.choices.input.value).toBe(''); expect(this.choices.input.value).toBe('');
@ -899,6 +926,10 @@ describe('Choices', () => {
document.body.appendChild(this.input); document.body.appendChild(this.input);
}); });
afterEach(function() {
this.choices.destroy();
});
it('should flip the dropdown', function() { it('should flip the dropdown', function() {
this.choices = new Choices(this.input, { this.choices = new Choices(this.input, {
position: 'top' position: 'top'
@ -919,4 +950,66 @@ describe('Choices', () => {
expect(container.classList.contains(this.choices.config.classNames.flippedState)).toBe(false); 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() {
var randomItem = {
id: 8999,
choiceId: 9000,
groupId: 9001,
value: 'value',
label: 'label',
customProperties: {
foo: 'bar'
}
}
var expectedState = [{
id: randomItem.id,
choiceId: randomItem.choiceId,
groupId: randomItem.groupId,
value: randomItem.value,
label: randomItem.label,
active: true,
highlighted: false,
customProperties: randomItem.customProperties
}];
var 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() {
var randomChoice = {
id: 123,
elementId: 321,
groupId: 213,
value: 'value',
label: 'label',
disabled: false,
customProperties: {
foo: 'bar'
}
}
var 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
}];
var action = addChoiceAction(randomChoice.value, randomChoice.label, randomChoice.id, randomChoice.groupId, randomChoice.disabled, randomChoice.elementId, randomChoice.customProperties);
expect(choiceReducer([], action)).toEqual(expectedState);
});
});
}); });