mirror of
https://github.com/Choices-js/Choices.git
synced 2024-05-05 07:13:11 +02:00
Resolve undefined error (#528)
* Remove run-p from test command * Remove dropdown interaction tests * Tidy utils * Use merge lib * Remove string casting * Sanitise in constants * Housekeeping * Add non-string value tests
This commit is contained in:
parent
5d4d3be1b5
commit
879c97f64c
|
@ -42,6 +42,7 @@
|
||||||
"no-plusplus": "off",
|
"no-plusplus": "off",
|
||||||
"no-unused-expressions": "off",
|
"no-unused-expressions": "off",
|
||||||
"no-underscore-dangle": "off",
|
"no-underscore-dangle": "off",
|
||||||
|
"consistent-return": "off",
|
||||||
"prettier/prettier": [
|
"prettier/prettier": [
|
||||||
"error",
|
"error",
|
||||||
{
|
{
|
||||||
|
|
|
@ -68,7 +68,7 @@ describe('Choices - select multiple', () => {
|
||||||
.click();
|
.click();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('allows me select choices from a dropdown', () => {
|
it('allows selecting choices from dropdown', () => {
|
||||||
cy.get('[data-test-hook=basic]')
|
cy.get('[data-test-hook=basic]')
|
||||||
.find('.choices__list--multiple .choices__item')
|
.find('.choices__list--multiple .choices__item')
|
||||||
.last()
|
.last()
|
||||||
|
@ -211,44 +211,6 @@ describe('Choices - select multiple', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
/*
|
|
||||||
There is currently a bug with opening/closing/toggling dropdowns
|
|
||||||
|
|
||||||
@todo Investigate why
|
|
||||||
*/
|
|
||||||
describe('interacting with dropdown', () => {
|
|
||||||
describe('opening dropdown', () => {
|
|
||||||
it('opens dropdown', () => {
|
|
||||||
cy.get('[data-test-hook=basic]')
|
|
||||||
.find('button.open-dropdown')
|
|
||||||
.focus()
|
|
||||||
.click();
|
|
||||||
|
|
||||||
cy.get('[data-test-hook=basic]')
|
|
||||||
.find('.choices__list--dropdown')
|
|
||||||
.should('be.visible');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('closing dropdown', () => {
|
|
||||||
it('closes dropdown', () => {
|
|
||||||
cy.get('[data-test-hook=basic]')
|
|
||||||
.find('button.open-dropdown')
|
|
||||||
.focus()
|
|
||||||
.click();
|
|
||||||
|
|
||||||
cy.get('[data-test-hook=basic]')
|
|
||||||
.find('button.close-dropdown')
|
|
||||||
.focus()
|
|
||||||
.click();
|
|
||||||
|
|
||||||
cy.get('[data-test-hook=basic]')
|
|
||||||
.find('.choices__list--dropdown')
|
|
||||||
.should('not.be.visible');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('disabling', () => {
|
describe('disabling', () => {
|
||||||
describe('on disable', () => {
|
describe('on disable', () => {
|
||||||
it('disables the search input', () => {
|
it('disables the search input', () => {
|
||||||
|
@ -754,6 +716,40 @@ describe('Choices - select multiple', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('non-string values', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
cy.get('[data-test-hook=non-string-values]')
|
||||||
|
.find('.choices')
|
||||||
|
.click();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('displays expected amount of choices in dropdown', () => {
|
||||||
|
cy.get('[data-test-hook=non-string-values]')
|
||||||
|
.find('.choices__list--dropdown .choices__list')
|
||||||
|
.children()
|
||||||
|
.should('have.length', 4);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('allows selecting choices from dropdown', () => {
|
||||||
|
let $selectedChoice;
|
||||||
|
cy.get('[data-test-hook=non-string-values]')
|
||||||
|
.find('.choices__list--dropdown .choices__list')
|
||||||
|
.children()
|
||||||
|
.first()
|
||||||
|
.then($choice => {
|
||||||
|
$selectedChoice = $choice;
|
||||||
|
})
|
||||||
|
.click();
|
||||||
|
|
||||||
|
cy.get('[data-test-hook=non-string-values]')
|
||||||
|
.find('.choices__list--single .choices__item')
|
||||||
|
.last()
|
||||||
|
.should($item => {
|
||||||
|
expect($item.text().trim()).to.equal($selectedChoice.text().trim());
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('within form', () => {
|
describe('within form', () => {
|
||||||
describe('selecting choice', () => {
|
describe('selecting choice', () => {
|
||||||
describe('on enter key', () => {
|
describe('on enter key', () => {
|
||||||
|
|
|
@ -45,7 +45,7 @@ describe('Choices - select one', () => {
|
||||||
describe('selecting choices', () => {
|
describe('selecting choices', () => {
|
||||||
const selectedChoiceText = 'Choice 1';
|
const selectedChoiceText = 'Choice 1';
|
||||||
|
|
||||||
it('allows me select choices from a dropdown', () => {
|
it('allows selecting choices from dropdown', () => {
|
||||||
cy.get('[data-test-hook=basic]')
|
cy.get('[data-test-hook=basic]')
|
||||||
.find('.choices__list--dropdown .choices__list')
|
.find('.choices__list--dropdown .choices__list')
|
||||||
.children()
|
.children()
|
||||||
|
@ -129,44 +129,6 @@ describe('Choices - select one', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
/*
|
|
||||||
There is currently a bug with opening/closing/toggling dropdowns
|
|
||||||
|
|
||||||
@todo Investigate why
|
|
||||||
*/
|
|
||||||
describe('interacting with dropdown', () => {
|
|
||||||
describe('opening dropdown', () => {
|
|
||||||
it('opens dropdown', () => {
|
|
||||||
cy.get('[data-test-hook=basic]')
|
|
||||||
.find('button.open-dropdown')
|
|
||||||
.focus()
|
|
||||||
.click();
|
|
||||||
|
|
||||||
cy.get('[data-test-hook=basic]')
|
|
||||||
.find('.choices__list--dropdown')
|
|
||||||
.should('be.visible');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('closing dropdown', () => {
|
|
||||||
it('closes dropdown', () => {
|
|
||||||
cy.get('[data-test-hook=basic]')
|
|
||||||
.find('button.open-dropdown')
|
|
||||||
.focus()
|
|
||||||
.click();
|
|
||||||
|
|
||||||
cy.get('[data-test-hook=basic]')
|
|
||||||
.find('button.close-dropdown')
|
|
||||||
.focus()
|
|
||||||
.click();
|
|
||||||
|
|
||||||
cy.get('[data-test-hook=basic]')
|
|
||||||
.find('.choices__list--dropdown')
|
|
||||||
.should('not.be.visible');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('disabling', () => {
|
describe('disabling', () => {
|
||||||
describe('on disable', () => {
|
describe('on disable', () => {
|
||||||
it('disables the search input', () => {
|
it('disables the search input', () => {
|
||||||
|
@ -418,7 +380,7 @@ describe('Choices - select one', () => {
|
||||||
.should('not.exist');
|
.should('not.exist');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('allows me select choices from a dropdown', () => {
|
it('allows selecting choices from dropdown', () => {
|
||||||
cy.get('[data-test-hook=search-disabled]')
|
cy.get('[data-test-hook=search-disabled]')
|
||||||
.find('.choices__list--dropdown .choices__list')
|
.find('.choices__list--dropdown .choices__list')
|
||||||
.children()
|
.children()
|
||||||
|
@ -766,6 +728,40 @@ describe('Choices - select one', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('non-string values', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
cy.get('[data-test-hook=non-string-values]')
|
||||||
|
.find('.choices')
|
||||||
|
.click();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('displays expected amount of choices in dropdown', () => {
|
||||||
|
cy.get('[data-test-hook=non-string-values]')
|
||||||
|
.find('.choices__list--dropdown .choices__list')
|
||||||
|
.children()
|
||||||
|
.should('have.length', 4);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('allows selecting choices from dropdown', () => {
|
||||||
|
let $selectedChoice;
|
||||||
|
cy.get('[data-test-hook=non-string-values]')
|
||||||
|
.find('.choices__list--dropdown .choices__list')
|
||||||
|
.children()
|
||||||
|
.first()
|
||||||
|
.then($choice => {
|
||||||
|
$selectedChoice = $choice;
|
||||||
|
})
|
||||||
|
.click();
|
||||||
|
|
||||||
|
cy.get('[data-test-hook=non-string-values]')
|
||||||
|
.find('.choices__list--single .choices__item')
|
||||||
|
.last()
|
||||||
|
.should($item => {
|
||||||
|
expect($item.text().trim()).to.equal($selectedChoice.text().trim());
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('within form', () => {
|
describe('within form', () => {
|
||||||
describe('selecting choice', () => {
|
describe('selecting choice', () => {
|
||||||
describe('on enter key', () => {
|
describe('on enter key', () => {
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
"bundlesize": "bundlesize",
|
"bundlesize": "bundlesize",
|
||||||
"cypress:run": "$(npm bin)/cypress run",
|
"cypress:run": "$(npm bin)/cypress run",
|
||||||
"cypress:open": "$(npm bin)/cypress open",
|
"cypress:open": "$(npm bin)/cypress open",
|
||||||
"test": "run-p test:unit test:e2e",
|
"test": "npm run test:unit && npm run test:e2e",
|
||||||
"test:unit": "mocha --require ./config/jsdom.js --require @babel/register $(find src -name '*.test.js') --exit",
|
"test:unit": "mocha --require ./config/jsdom.js --require @babel/register $(find src -name '*.test.js') --exit",
|
||||||
"test:unit:watch": "npm run test:unit -- --watch --inspect=5556",
|
"test:unit:watch": "npm run test:unit -- --watch --inspect=5556",
|
||||||
"test:e2e": "run-p --race start cypress:run",
|
"test:e2e": "run-p --race start cypress:run",
|
||||||
|
|
|
@ -31,8 +31,6 @@
|
||||||
<h2>Select multiple inputs</h2>
|
<h2>Select multiple inputs</h2>
|
||||||
<div data-test-hook="basic">
|
<div data-test-hook="basic">
|
||||||
<label for="choices-basic">Basic</label>
|
<label for="choices-basic">Basic</label>
|
||||||
<button class="open-dropdown push-bottom">Open dropdown</button>
|
|
||||||
<button class="close-dropdown push-bottom">Close dropdown</button>
|
|
||||||
<button class="disable push-bottom">Disable</button>
|
<button class="disable push-bottom">Disable</button>
|
||||||
<button class="enable push-bottom">Enable</button>
|
<button class="enable push-bottom">Enable</button>
|
||||||
<select class="form-control" name="choices-basic" id="choices-basic" multiple>
|
<select class="form-control" name="choices-basic" id="choices-basic" multiple>
|
||||||
|
@ -176,6 +174,11 @@
|
||||||
<select class="form-control" name="choices-custom-properties" id="choices-custom-properties" multiple></select>
|
<select class="form-control" name="choices-custom-properties" id="choices-custom-properties" multiple></select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div data-test-hook="non-string-values">
|
||||||
|
<label for="choices-non-string-values">Non-string values</label>
|
||||||
|
<select class="form-control" name="choices-non-string-values" id="choices-non-string-values"></select>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div data-test-hook="within-form">
|
<div data-test-hook="within-form">
|
||||||
<form>
|
<form>
|
||||||
<label for="choices-within-form">Within form</label>
|
<label for="choices-within-form">Within form</label>
|
||||||
|
@ -207,14 +210,6 @@
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
const choicesBasic = new Choices('#choices-basic');
|
const choicesBasic = new Choices('#choices-basic');
|
||||||
|
|
||||||
document.querySelector('button.open-dropdown').addEventListener('click', () => {
|
|
||||||
choicesBasic.showDropdown();
|
|
||||||
});
|
|
||||||
|
|
||||||
document.querySelector('button.close-dropdown').addEventListener('click', () => {
|
|
||||||
choicesBasic.hideDropdown();
|
|
||||||
});
|
|
||||||
|
|
||||||
document.querySelector('button.disable').addEventListener('click', () => {
|
document.querySelector('button.disable').addEventListener('click', () => {
|
||||||
choicesBasic.disable();
|
choicesBasic.disable();
|
||||||
});
|
});
|
||||||
|
@ -303,8 +298,35 @@
|
||||||
customProperties: {
|
customProperties: {
|
||||||
country: 'Portugal',
|
country: 'Portugal',
|
||||||
},
|
},
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
new Choices('#choices-non-string-values', {
|
||||||
|
choices: [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
label: 'Number',
|
||||||
|
value: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
label: 'Boolean',
|
||||||
|
value: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
label: 'Object',
|
||||||
|
value: {
|
||||||
|
test: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 4,
|
||||||
|
label: 'Array',
|
||||||
|
value: ['test'],
|
||||||
|
},
|
||||||
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
new Choices('#choices-within-form');
|
new Choices('#choices-within-form');
|
||||||
|
|
|
@ -31,8 +31,6 @@
|
||||||
<h2>Select one inputs</h2>
|
<h2>Select one inputs</h2>
|
||||||
<div data-test-hook="basic">
|
<div data-test-hook="basic">
|
||||||
<label for="choices-basic">Basic</label>
|
<label for="choices-basic">Basic</label>
|
||||||
<button class="open-dropdown push-bottom">Open dropdown</button>
|
|
||||||
<button class="close-dropdown push-bottom">Close dropdown</button>
|
|
||||||
<button class="disable push-bottom">Disable</button>
|
<button class="disable push-bottom">Disable</button>
|
||||||
<button class="enable push-bottom">Enable</button>
|
<button class="enable push-bottom">Enable</button>
|
||||||
<select class="form-control" name="choices-basic" id="choices-basic">
|
<select class="form-control" name="choices-basic" id="choices-basic">
|
||||||
|
@ -180,6 +178,11 @@
|
||||||
<select class="form-control" name="choices-custom-properties" id="choices-custom-properties"></select>
|
<select class="form-control" name="choices-custom-properties" id="choices-custom-properties"></select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div data-test-hook="non-string-values">
|
||||||
|
<label for="choices-non-string-values">Non-string values</label>
|
||||||
|
<select class="form-control" name="choices-non-string-values" id="choices-non-string-values"></select>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div data-test-hook="within-form">
|
<div data-test-hook="within-form">
|
||||||
<form>
|
<form>
|
||||||
<label for="choices-within-form">Within form</label>
|
<label for="choices-within-form">Within form</label>
|
||||||
|
@ -211,14 +214,6 @@
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
const choicesBasic = new Choices('#choices-basic');
|
const choicesBasic = new Choices('#choices-basic');
|
||||||
|
|
||||||
document.querySelector('button.open-dropdown').addEventListener('click', () => {
|
|
||||||
choicesBasic.showDropdown();
|
|
||||||
});
|
|
||||||
|
|
||||||
document.querySelector('button.close-dropdown').addEventListener('click', () => {
|
|
||||||
choicesBasic.hideDropdown();
|
|
||||||
});
|
|
||||||
|
|
||||||
document.querySelector('button.disable').addEventListener('click', () => {
|
document.querySelector('button.disable').addEventListener('click', () => {
|
||||||
choicesBasic.disable();
|
choicesBasic.disable();
|
||||||
});
|
});
|
||||||
|
@ -319,6 +314,33 @@
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|
||||||
|
new Choices('#choices-non-string-values', {
|
||||||
|
choices: [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
label: 'Number',
|
||||||
|
value: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
label: 'Boolean',
|
||||||
|
value: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
label: 'Object',
|
||||||
|
value: {
|
||||||
|
test: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 4,
|
||||||
|
label: 'Array',
|
||||||
|
value: ['test'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
new Choices('#choices-within-form');
|
new Choices('#choices-within-form');
|
||||||
|
|
||||||
new Choices('#choices-set-choice-by-value').setChoiceByValue('Choice 2');
|
new Choices('#choices-set-choice-by-value').setChoiceByValue('Choice 2');
|
||||||
|
|
|
@ -29,7 +29,6 @@ import {
|
||||||
getType,
|
getType,
|
||||||
isType,
|
isType,
|
||||||
strToEl,
|
strToEl,
|
||||||
extend,
|
|
||||||
sortByScore,
|
sortByScore,
|
||||||
generateId,
|
generateId,
|
||||||
findAncestorByAttrName,
|
findAncestorByAttrName,
|
||||||
|
@ -1787,7 +1786,7 @@ class Choices {
|
||||||
userTemplates = callbackOnCreateTemplates.call(this, strToEl);
|
userTemplates = callbackOnCreateTemplates.call(this, strToEl);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.config.templates = extend(TEMPLATES, userTemplates);
|
this.config.templates = merge(TEMPLATES, userTemplates);
|
||||||
}
|
}
|
||||||
|
|
||||||
_createElements() {
|
_createElements() {
|
||||||
|
|
|
@ -1,15 +1,12 @@
|
||||||
import { calcWidthOfInput, stripHTML } from '../lib/utils';
|
import { calcWidthOfInput, sanitise } from '../lib/utils';
|
||||||
|
|
||||||
export default class Input {
|
export default class Input {
|
||||||
constructor({ element, type, classNames, placeholderValue }) {
|
constructor({ element, type, classNames, placeholderValue }) {
|
||||||
Object.assign(this, { element, type, classNames, placeholderValue });
|
Object.assign(this, { element, type, classNames, placeholderValue });
|
||||||
|
|
||||||
this.element = element;
|
this.element = element;
|
||||||
this.classNames = classNames;
|
this.classNames = classNames;
|
||||||
this.isFocussed = this.element === document.activeElement;
|
this.isFocussed = this.element === document.activeElement;
|
||||||
this.isDisabled = false;
|
this.isDisabled = false;
|
||||||
|
|
||||||
// Bind event listeners
|
|
||||||
this._onPaste = this._onPaste.bind(this);
|
this._onPaste = this._onPaste.bind(this);
|
||||||
this._onInput = this._onInput.bind(this);
|
this._onInput = this._onInput.bind(this);
|
||||||
this._onFocus = this._onFocus.bind(this);
|
this._onFocus = this._onFocus.bind(this);
|
||||||
|
@ -21,11 +18,11 @@ export default class Input {
|
||||||
}
|
}
|
||||||
|
|
||||||
set value(value) {
|
set value(value) {
|
||||||
this.element.value = `${value}`;
|
this.element.value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
get value() {
|
get value() {
|
||||||
return stripHTML(this.element.value);
|
return sanitise(this.element.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
addEventListeners() {
|
addEventListeners() {
|
||||||
|
@ -134,7 +131,6 @@ export default class Input {
|
||||||
|
|
||||||
_onPaste(event) {
|
_onPaste(event) {
|
||||||
const { target } = event;
|
const { target } = event;
|
||||||
// Disable pasting into the input if option has been set
|
|
||||||
if (target === this.element && this.preventPaste) {
|
if (target === this.element && this.preventPaste) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import WrappedElement from './wrapped-element';
|
import WrappedElement from './wrapped-element';
|
||||||
import { reduceToValues } from './../lib/utils';
|
|
||||||
|
|
||||||
export default class WrappedInput extends WrappedElement {
|
export default class WrappedInput extends WrappedElement {
|
||||||
constructor({ element, classNames, delimiter }) {
|
constructor({ element, classNames, delimiter }) {
|
||||||
|
@ -8,11 +7,11 @@ export default class WrappedInput extends WrappedElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
set value(items) {
|
set value(items) {
|
||||||
const itemsFiltered = reduceToValues(items);
|
const itemValues = items.map(({ value }) => value);
|
||||||
const itemsFilteredString = itemsFiltered.join(this.delimiter);
|
const joinedValues = itemValues.join(this.delimiter);
|
||||||
|
|
||||||
this.element.setAttribute('value', itemsFilteredString);
|
this.element.setAttribute('value', joinedValues);
|
||||||
this.element.value = itemsFilteredString;
|
this.element.value = joinedValues;
|
||||||
}
|
}
|
||||||
|
|
||||||
// @todo figure out why we need this? Perhaps a babel issue
|
// @todo figure out why we need this? Perhaps a babel issue
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { stripHTML, sortByAlpha } from './lib/utils';
|
import { sanitise, sortByAlpha } from './lib/utils';
|
||||||
|
|
||||||
export const DEFAULT_CLASSNAMES = {
|
export const DEFAULT_CLASSNAMES = {
|
||||||
containerOuter: 'choices',
|
containerOuter: 'choices',
|
||||||
|
@ -65,7 +65,7 @@ export const DEFAULT_CONFIG = {
|
||||||
itemSelectText: 'Press to select',
|
itemSelectText: 'Press to select',
|
||||||
uniqueItemText: 'Only unique values can be added',
|
uniqueItemText: 'Only unique values can be added',
|
||||||
customAddItemText: 'Only values matching specific conditions can be added',
|
customAddItemText: 'Only values matching specific conditions can be added',
|
||||||
addItemText: value => `Press Enter to add <b>"${stripHTML(value)}"</b>`,
|
addItemText: value => `Press Enter to add <b>"${sanitise(value)}"</b>`,
|
||||||
maxItemText: maxItemCount => `Only ${maxItemCount} values can be added`,
|
maxItemText: maxItemCount => `Only ${maxItemCount} values can be added`,
|
||||||
itemComparer: (choice, item) => choice === item,
|
itemComparer: (choice, item) => choice === item,
|
||||||
fuseOptions: {
|
fuseOptions: {
|
||||||
|
|
|
@ -1,10 +1,7 @@
|
||||||
/* eslint-disable */
|
export const getRandomNumber = (min, max) =>
|
||||||
|
Math.floor(Math.random() * (max - min) + min);
|
||||||
|
|
||||||
export const getRandomNumber = function(min, max) {
|
export const generateChars = length => {
|
||||||
return Math.floor(Math.random() * (max - min) + min);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const generateChars = function(length) {
|
|
||||||
let chars = '';
|
let chars = '';
|
||||||
|
|
||||||
for (let i = 0; i < length; i++) {
|
for (let i = 0; i < length; i++) {
|
||||||
|
@ -15,7 +12,7 @@ export const generateChars = function(length) {
|
||||||
return chars;
|
return chars;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const generateId = function(element, prefix) {
|
export const generateId = (element, prefix) => {
|
||||||
let id =
|
let id =
|
||||||
element.id ||
|
element.id ||
|
||||||
(element.name && `${element.name}-${generateChars(2)}`) ||
|
(element.name && `${element.name}-${generateChars(2)}`) ||
|
||||||
|
@ -26,56 +23,14 @@ export const generateId = function(element, prefix) {
|
||||||
return id;
|
return id;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getType = function(obj) {
|
export const getType = obj => Object.prototype.toString.call(obj).slice(8, -1);
|
||||||
return Object.prototype.toString.call(obj).slice(8, -1);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const isType = function(type, obj) {
|
export const isType = (type, obj) =>
|
||||||
const clas = getType(obj);
|
obj !== undefined && obj !== null && getType(obj) === type;
|
||||||
return obj !== undefined && obj !== null && clas === type;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const isElement = (element) => {
|
export const isElement = element => element instanceof Element;
|
||||||
return element instanceof Element;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const extend = function() {
|
export const wrap = (element, wrapper = document.createElement('div')) => {
|
||||||
const extended = {};
|
|
||||||
const length = arguments.length;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Merge one object into another
|
|
||||||
* @param {Object} obj Object to merge into extended object
|
|
||||||
*/
|
|
||||||
const merge = function(obj) {
|
|
||||||
for (const prop in obj) {
|
|
||||||
if (Object.prototype.hasOwnProperty.call(obj, prop)) {
|
|
||||||
// If deep merge and property is an object, merge properties
|
|
||||||
if (isType('Object', obj[prop])) {
|
|
||||||
extended[prop] = extend(true, extended[prop], obj[prop]);
|
|
||||||
} else {
|
|
||||||
extended[prop] = obj[prop];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Loop through each passed argument
|
|
||||||
for (let i = 0; i < length; i++) {
|
|
||||||
// store argument at position i
|
|
||||||
const obj = arguments[i];
|
|
||||||
|
|
||||||
// If we are in fact dealing with an object, merge it.
|
|
||||||
if (isType('Object', obj)) {
|
|
||||||
merge(obj);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return extended;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const wrap = function(element, wrapper) {
|
|
||||||
wrapper = wrapper || document.createElement('div');
|
|
||||||
if (element.nextSibling) {
|
if (element.nextSibling) {
|
||||||
element.parentNode.insertBefore(wrapper, element.nextSibling);
|
element.parentNode.insertBefore(wrapper, element.nextSibling);
|
||||||
} else {
|
} else {
|
||||||
|
@ -84,12 +39,7 @@ export const wrap = function(element, wrapper) {
|
||||||
return wrapper.appendChild(element);
|
return wrapper.appendChild(element);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const findAncestor = function(el, cls) {
|
export const findAncestorByAttrName = (el, attr) => {
|
||||||
while ((el = el.parentElement) && !el.classList.contains(cls));
|
|
||||||
return el;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const findAncestorByAttrName = function(el, attr) {
|
|
||||||
let target = el;
|
let target = el;
|
||||||
|
|
||||||
while (target) {
|
while (target) {
|
||||||
|
@ -104,7 +54,9 @@ export const findAncestorByAttrName = function(el, attr) {
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getAdjacentEl = (startEl, className, direction = 1) => {
|
export const getAdjacentEl = (startEl, className, direction = 1) => {
|
||||||
if (!startEl || !className) return;
|
if (!startEl || !className) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const parent = startEl.parentNode.parentNode;
|
const parent = startEl.parentNode.parentNode;
|
||||||
const children = Array.from(parent.querySelectorAll(className));
|
const children = Array.from(parent.querySelectorAll(className));
|
||||||
|
@ -116,7 +68,9 @@ export const getAdjacentEl = (startEl, className, direction = 1) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
export const isScrolledIntoView = (el, parent, direction = 1) => {
|
export const isScrolledIntoView = (el, parent, direction = 1) => {
|
||||||
if (!el) return;
|
if (!el) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let isVisible;
|
let isVisible;
|
||||||
|
|
||||||
|
@ -132,26 +86,30 @@ export const isScrolledIntoView = (el, parent, direction = 1) => {
|
||||||
return isVisible;
|
return isVisible;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const stripHTML = html =>
|
export const sanitise = value => {
|
||||||
html
|
if (!isType('String', value)) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return value
|
||||||
.replace(/&/g, '&')
|
.replace(/&/g, '&')
|
||||||
.replace(/>/g, '&rt;')
|
.replace(/>/g, '&rt;')
|
||||||
.replace(/</g, '<')
|
.replace(/</g, '<')
|
||||||
.replace(/"/g, '"');
|
.replace(/"/g, '"');
|
||||||
|
};
|
||||||
|
|
||||||
export const strToEl = (function() {
|
export const strToEl = (() => {
|
||||||
const tmpEl = document.createElement('div');
|
const tmpEl = document.createElement('div');
|
||||||
return function(str) {
|
return str => {
|
||||||
const cleanedInput = str.trim();
|
const cleanedInput = str.trim();
|
||||||
let r;
|
|
||||||
tmpEl.innerHTML = cleanedInput;
|
tmpEl.innerHTML = cleanedInput;
|
||||||
r = tmpEl.children[0];
|
const firldChild = tmpEl.children[0];
|
||||||
|
|
||||||
while (tmpEl.firstChild) {
|
while (tmpEl.firstChild) {
|
||||||
tmpEl.removeChild(tmpEl.firstChild);
|
tmpEl.removeChild(tmpEl.firstChild);
|
||||||
}
|
}
|
||||||
|
|
||||||
return r;
|
return firldChild;
|
||||||
};
|
};
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
@ -164,7 +122,7 @@ export const calcWidthOfInput = (input, callback) => {
|
||||||
let width = input.offsetWidth;
|
let width = input.offsetWidth;
|
||||||
|
|
||||||
if (value) {
|
if (value) {
|
||||||
const testEl = strToEl(`<span>${stripHTML(value)}</span>`);
|
const testEl = strToEl(`<span>${sanitise(value)}</span>`);
|
||||||
testEl.style.position = 'absolute';
|
testEl.style.position = 'absolute';
|
||||||
testEl.style.padding = '0';
|
testEl.style.padding = '0';
|
||||||
testEl.style.top = '-9999px';
|
testEl.style.top = '-9999px';
|
||||||
|
@ -203,8 +161,8 @@ export const calcWidthOfInput = (input, callback) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
export const sortByAlpha = (a, b) => {
|
export const sortByAlpha = (a, b) => {
|
||||||
const labelA = (a.label || a.value).toLowerCase();
|
const labelA = `${a.label || a.value}`.toLowerCase();
|
||||||
const labelB = (b.label || b.value).toLowerCase();
|
const labelB = `${b.label || b.value}`.toLowerCase();
|
||||||
|
|
||||||
if (labelA < labelB) {
|
if (labelA < labelB) {
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -241,15 +199,6 @@ export const getWindowHeight = () => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const reduceToValues = (items, key = 'value') => {
|
|
||||||
const values = items.reduce((prev, current) => {
|
|
||||||
prev.push(current[key]);
|
|
||||||
return prev;
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return values;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const fetchFromObject = (object, path) => {
|
export const fetchFromObject = (object, path) => {
|
||||||
const index = path.indexOf('.');
|
const index = path.indexOf('.');
|
||||||
|
|
||||||
|
@ -284,7 +233,5 @@ export const diff = (a, b) => {
|
||||||
const aKeys = Object.keys(a).sort();
|
const aKeys = Object.keys(a).sort();
|
||||||
const bKeys = Object.keys(b).sort();
|
const bKeys = Object.keys(b).sort();
|
||||||
|
|
||||||
return aKeys.filter((i) => {
|
return aKeys.filter(i => bKeys.indexOf(i) < 0);
|
||||||
return bKeys.indexOf(i) < 0;
|
};
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,14 +1,13 @@
|
||||||
import { expect } from 'chai';
|
import { expect } from 'chai';
|
||||||
import { stub } from 'sinon';
|
import { stub } from 'sinon';
|
||||||
import {
|
import {
|
||||||
reduceToValues,
|
|
||||||
getRandomNumber,
|
getRandomNumber,
|
||||||
generateChars,
|
generateChars,
|
||||||
generateId,
|
generateId,
|
||||||
getType,
|
getType,
|
||||||
isType,
|
isType,
|
||||||
isElement,
|
isElement,
|
||||||
stripHTML,
|
sanitise,
|
||||||
sortByAlpha,
|
sortByAlpha,
|
||||||
sortByScore,
|
sortByScore,
|
||||||
fetchFromObject,
|
fetchFromObject,
|
||||||
|
@ -18,54 +17,6 @@ import {
|
||||||
} from './utils';
|
} from './utils';
|
||||||
|
|
||||||
describe('utils', () => {
|
describe('utils', () => {
|
||||||
describe('reduceToValues', () => {
|
|
||||||
const items = [
|
|
||||||
{
|
|
||||||
id: 1,
|
|
||||||
choiceId: 1,
|
|
||||||
groupId: -1,
|
|
||||||
value: 'Item one',
|
|
||||||
label: 'Item one',
|
|
||||||
active: false,
|
|
||||||
highlighted: false,
|
|
||||||
customProperties: null,
|
|
||||||
placeholder: false,
|
|
||||||
keyCode: null,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 2,
|
|
||||||
choiceId: 2,
|
|
||||||
groupId: -1,
|
|
||||||
value: 'Item two',
|
|
||||||
label: 'Item two',
|
|
||||||
active: true,
|
|
||||||
highlighted: false,
|
|
||||||
customProperties: null,
|
|
||||||
placeholder: false,
|
|
||||||
keyCode: null,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 3,
|
|
||||||
choiceId: 3,
|
|
||||||
groupId: -1,
|
|
||||||
value: 'Item three',
|
|
||||||
label: 'Item three',
|
|
||||||
active: true,
|
|
||||||
highlighted: true,
|
|
||||||
customProperties: null,
|
|
||||||
placeholder: false,
|
|
||||||
keyCode: null,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
it('returns an array of item values', () => {
|
|
||||||
const expectedResponse = [items[0].value, items[1].value, items[2].value];
|
|
||||||
|
|
||||||
const actualResponse = reduceToValues(items);
|
|
||||||
expect(actualResponse).to.eql(expectedResponse);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('getRandomNumber', () => {
|
describe('getRandomNumber', () => {
|
||||||
it('returns random number between range', () => {
|
it('returns random number between range', () => {
|
||||||
for (let index = 0; index < 10; index++) {
|
for (let index = 0; index < 10; index++) {
|
||||||
|
@ -154,10 +105,10 @@ describe('utils', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('stripHTML', () => {
|
describe('sanitise', () => {
|
||||||
it('strips HTML from value', () => {
|
it('strips HTML from value', () => {
|
||||||
const value = '<script>somethingMalicious();</script>';
|
const value = '<script>somethingMalicious();</script>';
|
||||||
const output = stripHTML(value);
|
const output = sanitise(value);
|
||||||
expect(output).to.equal(
|
expect(output).to.equal(
|
||||||
'<script&rt;somethingMalicious();</script&rt;',
|
'<script&rt;somethingMalicious();</script&rt;',
|
||||||
);
|
);
|
||||||
|
@ -247,25 +198,6 @@ describe('utils', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('reduceToValues', () => {
|
|
||||||
it('reduces an array of objects to an array of values using given key', () => {
|
|
||||||
const values = [
|
|
||||||
{ name: 'The Strokes' },
|
|
||||||
{ name: 'Arctic Monkeys' },
|
|
||||||
{ name: 'Oasis' },
|
|
||||||
{ name: 'Tame Impala' },
|
|
||||||
];
|
|
||||||
|
|
||||||
const output = reduceToValues(values, 'name');
|
|
||||||
expect(output).to.eql([
|
|
||||||
'The Strokes',
|
|
||||||
'Arctic Monkeys',
|
|
||||||
'Oasis',
|
|
||||||
'Tame Impala',
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('fetchFromObject', () => {
|
describe('fetchFromObject', () => {
|
||||||
it('fetches value from object using given path', () => {
|
it('fetches value from object using given path', () => {
|
||||||
const object = {
|
const object = {
|
||||||
|
|
Loading…
Reference in a new issue