From 9777287b92431e651063ad37c3a8c0e46bc7e74a Mon Sep 17 00:00:00 2001 From: Josh Johnson Date: Mon, 18 Dec 2017 12:06:38 +0000 Subject: [PATCH] Ensure destroying instance reinstates options + tests --- src/scripts/src/choices.js | 23 ++------ src/scripts/src/components/container.js | 11 ++-- src/scripts/src/components/container.test.js | 35 ++++++------ src/scripts/src/components/dropdown.test.js | 5 ++ src/scripts/src/components/input.test.js | 5 ++ src/scripts/src/components/list.test.js | 5 ++ .../src/components/wrapped-element.test.js | 5 ++ src/scripts/src/components/wrapped-input.js | 10 +++- .../src/components/wrapped-input.test.js | 28 ++++++++++ src/scripts/src/components/wrapped-select.js | 26 +++++++-- .../src/components/wrapped-select.test.js | 5 ++ src/scripts/src/lib/utils.js | 11 +++- src/scripts/src/lib/utils.test.js | 56 +++++++++++++++++++ src/scripts/src/store/store.js | 13 ----- src/scripts/src/store/store.test.js | 11 ---- src/scripts/src/templates.js | 4 +- 16 files changed, 178 insertions(+), 75 deletions(-) create mode 100644 src/scripts/src/lib/utils.test.js diff --git a/src/scripts/src/choices.js b/src/scripts/src/choices.js index 9262f8b..d0c9dae 100644 --- a/src/scripts/src/choices.js +++ b/src/scripts/src/choices.js @@ -15,7 +15,6 @@ import { clearAll } from './actions/misc'; import { isScrolledIntoView, getAdjacentEl, - wrap, getType, isType, isElement, @@ -220,6 +219,10 @@ class Choices { this.passedElement.reveal(); this.containerOuter.unwrap(this.passedElement.element); + if (this.isSelectElement) { + this.passedElement.setOptions(this.presetChoices); + } + // Clear data store this.clearStore(); @@ -388,25 +391,11 @@ class Choices { } if (this.isTextElement) { - // Simplify store data to just values - const itemsFiltered = this.store.getItemsReducedToValues(items); - const itemsFilteredString = itemsFiltered.join(this.config.delimiter); - // Update the value of the hidden input - this.passedElement.setValue(itemsFilteredString); + this.passedElement.setValue(items); } else { - const selectedOptionsFragment = document.createDocumentFragment(); - const addOptionToFragment = (item) => { - // Create a standard select option - const option = this._getTemplate('option', item); - // Append it to fragment - selectedOptionsFragment.appendChild(option); - }; - - // Add each list item to list - items.forEach(item => addOptionToFragment(item)); // Update the options of the hidden input - this.passedElement.setOptions(selectedOptionsFragment); + this.passedElement.setOptions(items); } const addItemToFragment = (item) => { diff --git a/src/scripts/src/components/container.js b/src/scripts/src/components/container.js index e7e92ee..ca3dcb6 100644 --- a/src/scripts/src/components/container.js +++ b/src/scripts/src/components/container.js @@ -152,17 +152,16 @@ export default class Container { } wrap(element) { - this.wrappedElement = element; - wrap(this.wrappedElement, this.element); + wrap(element, this.element); } - unwrap() { - // Move passed element back to original position + unwrap(element) { + // Move passed element outside this element this.element.parentNode.insertBefore( - this.wrappedElement, + element, this.element, ); - // Remove container + // Remove this element this.element.parentNode.removeChild(this.element); } diff --git a/src/scripts/src/components/container.test.js b/src/scripts/src/components/container.test.js index 166e3ee..2dfbcb3 100644 --- a/src/scripts/src/components/container.test.js +++ b/src/scripts/src/components/container.test.js @@ -16,7 +16,14 @@ describe('components/container', () => { }; element = document.createElement('div'); element.id = 'container'; - instance = new Container(choicesInstance, element, DEFAULT_CLASSNAMES); + document.body.appendChild(element); + instance = new Container(choicesInstance, document.getElementById('container'), DEFAULT_CLASSNAMES); + }); + + afterEach(() => { + document.body.innerHTML = ''; + element = null; + instance = null; }); describe('constructor', () => { @@ -351,21 +358,15 @@ describe('components/container', () => { instance.wrap(document.querySelector('div#wrap-test')); expect(instance.element.querySelector('div#wrap-test')).to.equal(elementToWrap); }); - - it('assigns reference to element to instance', () => { - expect(instance.wrappedElement).to.equal(undefined); - instance.wrap(document.querySelector('div#wrap-test')); - expect(instance.wrappedElement).to.equal(elementToWrap); - }); }); describe('unwrap', () => { - let elementToWrap; + let elementToUnwrap; beforeEach(() => { - elementToWrap = document.createElement('div'); - elementToWrap.id = 'unwrap-test'; - document.body.appendChild(elementToWrap); + elementToUnwrap = document.createElement('div'); + elementToUnwrap.id = 'unwrap-test'; + document.body.appendChild(elementToUnwrap); instance.wrap(document.getElementById('unwrap-test')); }); @@ -375,16 +376,16 @@ describe('components/container', () => { it('moves wrapped element outside of element', () => { expect(instance.element.querySelector('div#unwrap-test')).to.be.instanceof(HTMLElement); - instance.unwrap(elementToWrap); + instance.unwrap(elementToUnwrap); expect(instance.element.querySelector('div#unwrap-test')).to.equal(null); expect(document.querySelector('div#unwrap-test')).to.be.instanceof(HTMLElement); }); - // it('removes element from DOM', () => { - // expect(document.getElementById('container')).to.not.equal(null); - // instance.unwrap(elementToWrap); - // expect(document.getElementById('container')).to.equal(null); - // }); + it('removes element from DOM', () => { + expect(document.getElementById('container')).to.not.equal(null); + instance.unwrap(elementToUnwrap); + expect(document.getElementById('container')).to.equal(null); + }); }); describe('addLoadingState', () => { diff --git a/src/scripts/src/components/dropdown.test.js b/src/scripts/src/components/dropdown.test.js index 6ab6b74..c8699ec 100644 --- a/src/scripts/src/components/dropdown.test.js +++ b/src/scripts/src/components/dropdown.test.js @@ -20,6 +20,11 @@ describe('components/dropdown', () => { instance = new Dropdown(choicesInstance, choicesElement, DEFAULT_CLASSNAMES); }); + afterEach(() => { + document.body.innerHTML = ''; + instance = null; + }); + describe('constructor', () => { it('assigns choices instance to instance', () => { expect(instance.parentInstance).to.eql(choicesInstance); diff --git a/src/scripts/src/components/input.test.js b/src/scripts/src/components/input.test.js index 924744c..2cc28c2 100644 --- a/src/scripts/src/components/input.test.js +++ b/src/scripts/src/components/input.test.js @@ -18,6 +18,11 @@ describe('components/input', () => { instance = new Input(choicesInstance, choicesElement, DEFAULT_CLASSNAMES); }); + afterEach(() => { + document.body.innerHTML = ''; + instance = null; + }); + describe('constructor', () => { it('assigns choices instance to class', () => { expect(instance.parentInstance).to.eql(choicesInstance); diff --git a/src/scripts/src/components/list.test.js b/src/scripts/src/components/list.test.js index de51483..5f6b33a 100644 --- a/src/scripts/src/components/list.test.js +++ b/src/scripts/src/components/list.test.js @@ -17,6 +17,11 @@ describe('components/list', () => { instance = new List(choicesInstance, choicesElement, DEFAULT_CLASSNAMES); }); + afterEach(() => { + document.body.innerHTML = ''; + instance = null; + }); + describe('constructor', () => { it('assigns choices instance to class', () => { expect(instance.parentInstance).to.eql(choicesInstance); diff --git a/src/scripts/src/components/wrapped-element.test.js b/src/scripts/src/components/wrapped-element.test.js index e786f42..41feba8 100644 --- a/src/scripts/src/components/wrapped-element.test.js +++ b/src/scripts/src/components/wrapped-element.test.js @@ -18,6 +18,11 @@ describe('components/wrappedElement', () => { instance = new WrappedElement(choicesInstance, choicesElement, DEFAULT_CLASSNAMES); }); + afterEach(() => { + document.body.innerHTML = ''; + instance = null; + }); + describe('getElement', () => { it('returns DOM reference of element', () => { expect(instance.getElement()).to.eql(choicesElement); diff --git a/src/scripts/src/components/wrapped-input.js b/src/scripts/src/components/wrapped-input.js index 34d24ef..a874a59 100644 --- a/src/scripts/src/components/wrapped-input.js +++ b/src/scripts/src/components/wrapped-input.js @@ -1,4 +1,5 @@ import WrappedElement from './wrapped-element'; +import { reduceToValues } from './../lib/utils'; export default class WrappedInput extends WrappedElement { constructor(instance, element, classNames) { @@ -28,8 +29,11 @@ export default class WrappedInput extends WrappedElement { super.enable(); } - setValue(value) { - this.element.setAttribute('value', value); - this.element.value = value; + setValue(items) { + const itemsFiltered = reduceToValues(items); + const itemsFilteredString = itemsFiltered.join(this.parentInstance.config.delimiter); + + this.element.setAttribute('value', itemsFilteredString); + this.element.value = itemsFilteredString; } } diff --git a/src/scripts/src/components/wrapped-input.test.js b/src/scripts/src/components/wrapped-input.test.js index 1e77aea..8c79ebd 100644 --- a/src/scripts/src/components/wrapped-input.test.js +++ b/src/scripts/src/components/wrapped-input.test.js @@ -16,4 +16,32 @@ describe('components/wrappedInput', () => { choicesElement = document.createElement('input'); instance = new WrappedInput(choicesInstance, choicesElement, DEFAULT_CLASSNAMES); }); + + afterEach(() => { + document.body.innerHTML = ''; + instance = null; + }); + + describe('setValue', () => { + const data = [ + { + id: 'ID 1', + value: 'Value 1', + }, + { + id: 'ID 2', + value: 'Value 2', + }, + { + id: 'ID 3', + value: 'Value 3', + }, + ]; + + it('sets delimited value of element based on passed data', () => { + expect(instance.element.value).to.equal(''); + instance.setValue(data); + expect(instance.element.value).to.equal('Value 1,Value 2,Value 3'); + }); + }); }); diff --git a/src/scripts/src/components/wrapped-select.js b/src/scripts/src/components/wrapped-select.js index c7594ca..a437ce4 100644 --- a/src/scripts/src/components/wrapped-select.js +++ b/src/scripts/src/components/wrapped-select.js @@ -1,4 +1,5 @@ import WrappedElement from './wrapped-element'; +import templates from './../templates'; export default class WrappedSelect extends WrappedElement { constructor(instance, element, classNames) { @@ -28,11 +29,6 @@ export default class WrappedSelect extends WrappedElement { super.enable(); } - setOptions(options) { - this.element.innerHTML = ''; - this.element.appendChild(options); - } - getPlaceholderOption() { return this.element.querySelector('option[placeholder]'); } @@ -44,4 +40,24 @@ export default class WrappedSelect extends WrappedElement { getOptionGroups() { return Array.from(this.element.getElementsByTagName('OPTGROUP')); } + + setOptions(options) { + const fragment = document.createDocumentFragment(); + const addOptionToFragment = (data) => { + // Create a standard select option + const template = templates.option(data); + // Append it to fragment + fragment.appendChild(template); + }; + + // Add each list item to list + options.forEach(optionData => addOptionToFragment(optionData)); + + this.appendDocFragment(fragment); + } + + appendDocFragment(fragment) { + this.element.innerHTML = ''; + this.element.appendChild(fragment); + } } diff --git a/src/scripts/src/components/wrapped-select.test.js b/src/scripts/src/components/wrapped-select.test.js index 41425d4..e9fb2f1 100644 --- a/src/scripts/src/components/wrapped-select.test.js +++ b/src/scripts/src/components/wrapped-select.test.js @@ -16,4 +16,9 @@ describe('components/wrappedSelect', () => { choicesElement = document.createElement('select'); instance = new WrappedSelect(choicesInstance, choicesElement, DEFAULT_CLASSNAMES); }); + + afterEach(() => { + document.body.innerHTML = ''; + instance = null; + }); }); diff --git a/src/scripts/src/lib/utils.js b/src/scripts/src/lib/utils.js index 797ed84..af1833d 100644 --- a/src/scripts/src/lib/utils.js +++ b/src/scripts/src/lib/utils.js @@ -593,4 +593,13 @@ export const getWindowHeight = () => { html.scrollHeight, html.offsetHeight, ); -}; \ No newline at end of file +}; + +export const reduceToValues = (items, key = 'value') => { + const values = items.reduce((prev, current) => { + prev.push(current[key]); + return prev; + }, []); + + return values; +} \ No newline at end of file diff --git a/src/scripts/src/lib/utils.test.js b/src/scripts/src/lib/utils.test.js new file mode 100644 index 0000000..ad7a68a --- /dev/null +++ b/src/scripts/src/lib/utils.test.js @@ -0,0 +1,56 @@ +import { expect } from 'chai'; +import { reduceToValues } from './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); + }); + }); +}); diff --git a/src/scripts/src/store/store.js b/src/scripts/src/store/store.js index c31f740..d23f427 100644 --- a/src/scripts/src/store/store.js +++ b/src/scripts/src/store/store.js @@ -68,19 +68,6 @@ export default class Store { return values; } - /** - * Get items from store reduced to just their values - * @return {Array} Item objects - */ - getItemsReducedToValues(items) { - const values = items.reduce((prev, current) => { - prev.push(current.value); - return prev; - }, []); - - return values; - } - /** * Get choices from store * @return {Array} Option objects diff --git a/src/scripts/src/store/store.test.js b/src/scripts/src/store/store.test.js index 4074c0e..1917d77 100644 --- a/src/scripts/src/store/store.test.js +++ b/src/scripts/src/store/store.test.js @@ -177,17 +177,6 @@ describe('reducers/store', () => { }); }); - describe('getItemsReducedToValues', () => { - it('returns an array of item values', () => { - const expectedResponse = state.items.reduce((items, item) => { - items.push(item.value); - return items; - }, []); - const actualResponse = instance.getItemsReducedToValues(state.items); - expect(actualResponse).to.eql(expectedResponse); - }); - }); - describe('getChoices', () => { it('returns choices', () => { const expectedResponse = state.choices; diff --git a/src/scripts/src/templates.js b/src/scripts/src/templates.js index 584cc17..ed532c7 100644 --- a/src/scripts/src/templates.js +++ b/src/scripts/src/templates.js @@ -226,9 +226,9 @@ export const TEMPLATES = { `); }, - option(globalClasses, data) { + option(data) { return strToEl(` - + `); }, };