diff --git a/src/scripts/src/choices.js b/src/scripts/src/choices.js index 63f184f..a154125 100644 --- a/src/scripts/src/choices.js +++ b/src/scripts/src/choices.js @@ -50,31 +50,31 @@ class Choices { } // Create data store - this.store = new Store(this.render); + this._store = new Store(this.render); // State tracking this.initialised = false; - this.currentState = {}; - this.prevState = {}; - this.currentValue = ''; - this.isScrollingOnIe = false; - this.wasTap = true; + this._currentState = {}; + this._prevState = {}; + this._currentValue = ''; + this._isScrollingOnIe = false; + this._wasTap = true; // Retrieve triggering element (i.e. element with 'data-choice' trigger) const passedElement = isType('String', element) ? document.querySelector(element) : element; - this.isTextElement = passedElement.type === 'text'; - this.isSelectOneElement = passedElement.type === 'select-one'; - this.isSelectMultipleElement = passedElement.type === 'select-multiple'; - this.isSelectElement = this.isSelectOneElement || this.isSelectMultipleElement; + this._isTextElement = passedElement.type === 'text'; + this._isSelectOneElement = passedElement.type === 'select-one'; + this._isSelectMultipleElement = passedElement.type === 'select-multiple'; + this._isSelectElement = this._isSelectOneElement || this._isSelectMultipleElement; - if (this.isTextElement) { + if (this._isTextElement) { this.passedElement = new WrappedInput({ element: passedElement, classNames: this.config.classNames, delimiter: this.config.delimiter, }); - } else if (this.isSelectElement) { + } else if (this._isSelectElement) { this.passedElement = new WrappedSelect({ element: passedElement, classNames: this.config.classNames, @@ -85,27 +85,27 @@ class Choices { throw new Error('Could not wrap passed element'); } - if (this.config.shouldSortItems === true && this.isSelectOneElement && !this.config.silent) { + if (this.config.shouldSortItems === true && this._isSelectOneElement && !this.config.silent) { console.warn('shouldSortElements: Type of passed element is \'select-one\', falling back to false.'); } - this.highlightPosition = 0; - this.placeholderValue = this._generatePlaceholderValue(); + this._highlightPosition = 0; + this._placeholderValue = this._generatePlaceholderValue(); // Assign preset choices from passed object - this.presetChoices = this.config.choices; + this._presetChoices = this.config.choices; // Assign preset items from passed object first - this.presetItems = this.config.items; + this._presetItems = this.config.items; // Then add any values passed from attribute if (this.passedElement.value) { - this.presetItems = this.presetItems.concat( + this._presetItems = this._presetItems.concat( this.passedElement.value.split(this.config.delimiter), ); } // Set unique base Id - this.baseId = generateId(this.passedElement.element, 'choices-'); + this._baseId = generateId(this.passedElement.element, 'choices-'); this.idNames = { itemChoice: 'item-choice', @@ -154,16 +154,16 @@ class Choices { // Generate input markup this._createStructure(); // Subscribe store to render method - this.store.subscribe(this.render); + this._store.subscribe(this.render); // Render any items this.render(); // Trigger event listeners this._addEventListeners(); - const callback = this.config.callbackOnInit; + const { callbackOnInit } = this.config; // Run callback if it is a function - if (callback && isType('Function', callback)) { - callback.call(this); + if (callbackOnInit && isType('Function', callbackOnInit)) { + callbackOnInit.call(this); } } @@ -182,8 +182,8 @@ class Choices { this.passedElement.reveal(); this.containerOuter.unwrap(this.passedElement.element); - if (this.isSelectElement) { - this.passedElement.options = this.presetChoices; + if (this._isSelectElement) { + this.passedElement.options = this._presetChoices; } // Clear data store @@ -243,14 +243,15 @@ class Choices { * @private */ render() { - this.currentState = this.store.state; + this._currentState = this._store.state; + const stateChanged = ( - this.currentState.choices !== this.prevState.choices || - this.currentState.groups !== this.prevState.groups || - this.currentState.items !== this.prevState.items + this._currentState.choices !== this._prevState.choices || + this._currentState.groups !== this._prevState.groups || + this._currentState.items !== this._prevState.items ); - const shouldRenderChoices = this.isSelectElement; - const shouldRenderItems = this.currentState.items !== this.prevState.items; + const shouldRenderChoices = this._isSelectElement; + const shouldRenderItems = this._currentState.items !== this._prevState.items; if (!stateChanged) { return; @@ -264,7 +265,7 @@ class Choices { this._renderItems(); } - this.prevState = this.currentState; + this._prevState = this._currentState; } /** @@ -280,9 +281,9 @@ class Choices { } const { id, groupId = -1, value = '', label = '' } = item; - const group = groupId >= 0 ? this.store.getGroupById(groupId) : null; + const group = groupId >= 0 ? this._store.getGroupById(groupId) : null; - this.store.dispatch(highlightItem(id, true)); + this._store.dispatch(highlightItem(id, true)); if (runEvent) { this.passedElement.triggerEvent(EVENTS.highlightItem, { @@ -308,9 +309,9 @@ class Choices { } const { id, groupId = -1, value = '', label = '' } = item; - const group = groupId >= 0 ? this.store.getGroupById(groupId) : null; + const group = groupId >= 0 ? this._store.getGroupById(groupId) : null; - this.store.dispatch(highlightItem(id, false)); + this._store.dispatch(highlightItem(id, false)); this.passedElement.triggerEvent(EVENTS.highlightItem, { id, value, @@ -327,7 +328,7 @@ class Choices { * @public */ highlightAll() { - this.store.items.forEach(item => this.highlightItem(item)); + this._store.items.forEach(item => this.highlightItem(item)); return this; } @@ -337,7 +338,7 @@ class Choices { * @public */ unhighlightAll() { - this.store.items.forEach(item => this.unhighlightItem(item)); + this._store.items.forEach(item => this.unhighlightItem(item)); return this; } @@ -353,7 +354,7 @@ class Choices { return this; } - this.store.activeItems + this._store.activeItems .filter(item => item.value === value) .forEach(item => this._removeItem(item)); @@ -368,7 +369,7 @@ class Choices { * @public */ removeActiveItems(excludedId) { - this.store.activeItems + this._store.activeItems .filter(({ id }) => id !== excludedId) .forEach(item => this._removeItem(item)); @@ -382,7 +383,7 @@ class Choices { * @public */ removeHighlightedItems(runEvent = false) { - this.store.highlightedActiveItems + this._store.highlightedActiveItems .forEach((item) => { this._removeItem(item); // If this action was performed by the user @@ -467,7 +468,7 @@ class Choices { * @public */ getValue(valueOnly = false) { - const items = this.store.activeItems; + const items = this._store.activeItems; const values = items.reduce((selectedItems, item) => { const itemValue = valueOnly ? item.value : item; @@ -475,7 +476,7 @@ class Choices { return selectedItems; }, []); - return this.isSelectOneElement ? values[0] : values; + return this._isSelectOneElement ? values[0] : values; } /** @@ -504,7 +505,7 @@ class Choices { * @public */ setChoiceByValue(value) { - if (!this.initialised || this.isTextElement) { + if (!this.initialised || this._isTextElement) { return this; } @@ -528,7 +529,7 @@ class Choices { */ setChoices(choices = [], value = '', label = '', replaceChoices = false) { if ( - !this.isSelectElement || + !this._isSelectElement || !choices.length || !value ) { @@ -574,7 +575,7 @@ class Choices { * @public */ clearStore() { - this.store.dispatch(clearAll()); + this._store.dispatch(clearAll()); return this; } @@ -584,12 +585,12 @@ class Choices { * @public */ clearInput() { - const shouldSetInputWidth = !this.isSelectOneElement; + const shouldSetInputWidth = !this._isSelectOneElement; this.input.clear(shouldSetInputWidth); - if (!this.isTextElement && this.config.searchEnabled) { - this.isSearching = false; - this.store.dispatch(activateChoices(true)); + if (!this._isTextElement && this.config.searchEnabled) { + this._isSearching = false; + this._store.dispatch(activateChoices(true)); } return this; @@ -602,7 +603,7 @@ class Choices { * @public */ ajax(fn) { - if (!this.initialised || !this.isSelectElement || !fn) { + if (!this.initialised || !this._isSelectElement || !fn) { return this; } @@ -629,7 +630,7 @@ class Choices { _createGroupsFragment(groups, choices, fragment) { const groupFragment = fragment || document.createDocumentFragment(); const getGroupChoices = group => choices.filter((choice) => { - if (this.isSelectOneElement) { + if (this._isSelectOneElement) { return choice.groupId === group.id; } return choice.groupId === group.id && (this.config.renderSelectedChoices === 'always' || !choice.selected); @@ -664,10 +665,10 @@ class Choices { // Create a fragment to store our list items (so we don't have to update the DOM for each item) const choicesFragment = fragment || document.createDocumentFragment(); const { renderSelectedChoices, searchResultLimit, renderChoiceLimit } = this.config; - const filter = this.isSearching ? sortByScore : this.config.sortFn; + const filter = this._isSearching ? sortByScore : this.config.sortFn; const appendChoice = (choice) => { const shouldRender = renderSelectedChoices === 'auto' ? - (this.isSelectOneElement || !choice.selected) : + (this._isSelectOneElement || !choice.selected) : true; if (shouldRender) { const dropdownItem = this._getTemplate('choice', choice, this.config.itemSelectText); @@ -677,7 +678,7 @@ class Choices { let rendererableChoices = choices; - if (renderSelectedChoices === 'auto' && !this.isSelectOneElement) { + if (renderSelectedChoices === 'auto' && !this._isSelectOneElement) { rendererableChoices = choices.filter(choice => !choice.selected); } @@ -692,7 +693,7 @@ class Choices { }, { placeholderChoices: [], normalChoices: [] }); // If sorting is enabled or the user is searching, filter choices - if (this.config.shouldSort || this.isSearching) { + if (this.config.shouldSort || this._isSearching) { normalChoices.sort(filter); } @@ -701,7 +702,7 @@ class Choices { // Prepend placeholeder const sortedChoices = [...placeholderChoices, ...normalChoices]; - if (this.isSearching) { + if (this._isSearching) { choiceLimit = searchResultLimit; } else if (renderChoiceLimit > 0 && !withinGroup) { choiceLimit = renderChoiceLimit; @@ -726,14 +727,15 @@ class Choices { */ _createItemsFragment(items, fragment = null) { // Create fragment to add elements to + const { shouldSortItems, sortFn, removeItemButton } = this.config; const itemListFragment = fragment || document.createDocumentFragment(); // If sorting is enabled, filter items - if (this.config.shouldSortItems && !this.isSelectOneElement) { - items.sort(this.config.sortFn); + if (shouldSortItems && !this._isSelectOneElement) { + items.sort(sortFn); } - if (this.isTextElement) { + if (this._isTextElement) { // Update the value of the hidden input this.passedElement.value = items; } else { @@ -743,7 +745,7 @@ class Choices { const addItemToFragment = (item) => { // Create new list element - const listItem = this._getTemplate('item', item, this.config.removeItemButton); + const listItem = this._getTemplate('item', item, removeItemButton); // Append it to list itemListFragment.appendChild(listItem); }; @@ -774,7 +776,7 @@ class Choices { * Select placeholder choice */ _selectPlaceholderChoice() { - const placeholderChoice = this.store.placeholderChoice; + const placeholderChoice = this._store.placeholderChoice; if (placeholderChoice) { this._addItem( @@ -810,11 +812,11 @@ class Choices { const itemId = element.parentNode.getAttribute('data-id'); const itemToRemove = activeItems.find(item => item.id === parseInt(itemId, 10)); - // Remove item associated with button + // Remove item associated with button this._removeItem(itemToRemove); this._triggerChange(itemToRemove.value); - if (this.isSelectOneElement) { + if (this._isSelectOneElement) { this._selectPlaceholderChoice(); } } @@ -832,7 +834,7 @@ class Choices { !activeItems || !element || !this.config.removeItems || - this.isSelectOneElement + this._isSelectOneElement ) { return; } @@ -868,7 +870,7 @@ class Choices { // If we are clicking on an option const id = element.getAttribute('data-id'); - const choice = this.store.getChoiceById(id); + const choice = this._store.getChoiceById(id); const passedKeyCode = activeItems[0] && activeItems[0].keyCode ? activeItems[0].keyCode : null; const hasActiveDropdown = this.dropdown.isActive; @@ -899,7 +901,7 @@ class Choices { this.clearInput(); // We wont to close the dropdown if we are dealing with a single select box - if (hasActiveDropdown && this.isSelectOneElement) { + if (hasActiveDropdown && this._isSelectOneElement) { this.hideDropdown(); this.containerOuter.focus(); } @@ -945,7 +947,7 @@ class Choices { let placeholderItem = this.itemList.getChild(`.${this.config.classNames.placeholder}`); if (isLoading) { this.containerOuter.addLoadingState(); - if (this.isSelectOneElement) { + if (this._isSelectOneElement) { if (!placeholderItem) { placeholderItem = this._getTemplate('placeholder', this.config.loadingText); this.itemList.append(placeholderItem); @@ -958,10 +960,10 @@ class Choices { } else { this.containerOuter.removeLoadingState(); - if (this.isSelectOneElement) { - placeholderItem.innerHTML = (this.placeholderValue || ''); + if (this._isSelectOneElement) { + placeholderItem.innerHTML = (this._placeholderValue || ''); } else { - this.input.placeholder = (this.placeholderValue || ''); + this.input.placeholder = (this._placeholderValue || ''); } } } @@ -980,7 +982,7 @@ class Choices { this.config.addItemText(value) : this.config.addItemText; - if (this.isSelectMultipleElement || this.isTextElement) { + if (this._isSelectMultipleElement || this._isTextElement) { if (this.config.maxItemCount > 0 && this.config.maxItemCount <= activeItems.length) { // If there is a max entry limit and we have reached that limit // don't update @@ -991,7 +993,7 @@ class Choices { } } - if (this.config.regexFilter && this.isTextElement && this.config.addItems && canAddItem) { + if (this.config.regexFilter && this._isTextElement && this.config.addItems && canAddItem) { // If a user has supplied a regular expression filter // determine whether we can update based on whether // our regular expression passes @@ -1010,7 +1012,7 @@ class Choices { if (!isUnique && !this.config.duplicateItems && - !this.isSelectOneElement && + !this._isSelectOneElement && canAddItem ) { canAddItem = false; @@ -1064,7 +1066,7 @@ class Choices { } }); - if (this.isSelectOneElement) { + if (this._isSelectOneElement) { this._selectPlaceholderChoice(); } } else { @@ -1082,16 +1084,16 @@ class Choices { */ _searchChoices(value) { const newValue = isType('String', value) ? value.trim() : value; - const currentValue = isType('String', this.currentValue) ? - this.currentValue.trim() : - this.currentValue; + const currentValue = isType('String', this._currentValue) ? + this._currentValue.trim() : + this._currentValue; if (newValue.length < 1 && newValue === `${currentValue} `) { return 0; } // If new value matches the desired length and is not the same as the current value with a space - const haystack = this.store.searchableChoices; + const haystack = this._store.searchableChoices; const needle = newValue; const keys = isType('Array', this.config.searchFields) ? this.config.searchFields : @@ -1100,10 +1102,10 @@ class Choices { const fuse = new Fuse(haystack, options); const results = fuse.search(needle); - this.currentValue = newValue; - this.highlightPosition = 0; - this.isSearching = true; - this.store.dispatch(filterChoices(results)); + this._currentValue = newValue; + this._highlightPosition = 0; + this._isSearching = true; + this._store.dispatch(filterChoices(results)); return results.length; } @@ -1119,12 +1121,13 @@ class Choices { return; } - const choices = this.store.choices; + const choices = this._store.choices; + const { searchFloor, searchChoices } = this.config; const hasUnactiveChoices = choices.some(option => !option.active); // Check that we have a value to search and the input was an alphanumeric character - if (value && value.length >= this.config.searchFloor) { - const resultCount = this.config.searchChoices ? this._searchChoices(value) : 0; + if (value && value.length >= searchFloor) { + const resultCount = searchChoices ? this._searchChoices(value) : 0; // Trigger search event this.passedElement.triggerEvent(EVENTS.search, { value, @@ -1132,8 +1135,8 @@ class Choices { }); } else if (hasUnactiveChoices) { // Otherwise reset choices to active - this.isSearching = false; - this.store.dispatch(activateChoices(true)); + this._isSearching = false; + this._store.dispatch(activateChoices(true)); } } @@ -1151,7 +1154,7 @@ class Choices { document.addEventListener('mousedown', this._onMouseDown); document.addEventListener('mouseover', this._onMouseOver); - if (this.isSelectOneElement) { + if (this._isSelectOneElement) { this.containerOuter.element.addEventListener('focus', this._onFocus); this.containerOuter.element.addEventListener('blur', this._onBlur); } @@ -1176,14 +1179,13 @@ class Choices { document.removeEventListener('mousedown', this._onMouseDown); document.removeEventListener('mouseover', this._onMouseOver); - if (this.isSelectOneElement) { + if (this._isSelectOneElement) { this.containerOuter.element.removeEventListener('focus', this._onFocus); this.containerOuter.element.removeEventListener('blur', this._onBlur); } this.input.element.removeEventListener('focus', this._onFocus); this.input.element.removeEventListener('blur', this._onBlur); - this.input.removeEventListeners(); } @@ -1198,7 +1200,7 @@ class Choices { } const target = e.target; - const activeItems = this.store.activeItems; + const activeItems = this._store.activeItems; const hasFocusedInput = this.input.isFocussed; const hasActiveDropdown = this.dropdown.isActive; const hasItems = this.itemList.hasChildren; @@ -1215,7 +1217,7 @@ class Choices { const ctrlDownKey = (e.ctrlKey || e.metaKey); // If a user is typing and the dropdown is not active - if (!this.isTextElement && /[a-zA-Z0-9-_ ]/.test(keyString)) { + if (!this._isTextElement && /[a-zA-Z0-9-_ ]/.test(keyString)) { this.showDropdown(true); } @@ -1238,7 +1240,7 @@ class Choices { const onEnterKey = () => { // If enter key is pressed and the input has a value - if (this.isTextElement && target.value) { + if (this._isTextElement && target.value) { const value = this.input.value; const canAddItem = this._canAddItem(activeItems, value); @@ -1268,7 +1270,7 @@ class Choices { } this._handleChoiceAction(activeItems, highlighted); } - } else if (this.isSelectOneElement) { + } else if (this._isSelectOneElement) { // Open single select dropdown if it's not active this.showDropdown(true); e.preventDefault(); @@ -1284,7 +1286,7 @@ class Choices { const onDirectionKey = () => { // If up or down key is pressed, traverse through options - if (hasActiveDropdown || this.isSelectOneElement) { + if (hasActiveDropdown || this._isSelectOneElement) { // Show dropdown if focus this.showDropdown(true); @@ -1292,24 +1294,25 @@ class Choices { const directionInt = e.keyCode === downKey || e.keyCode === pageDownKey ? 1 : -1; const skipKey = e.metaKey || e.keyCode === pageDownKey || e.keyCode === pageUpKey; + const selectableChoiceIdentifier = '[data-choice-selectable]'; let nextEl; if (skipKey) { if (directionInt > 0) { nextEl = Array.from( - this.dropdown.element.querySelectorAll('[data-choice-selectable]'), + this.dropdown.element.querySelectorAll(selectableChoiceIdentifier), ).pop(); } else { - nextEl = this.dropdown.element.querySelector('[data-choice-selectable]'); + nextEl = this.dropdown.element.querySelector(selectableChoiceIdentifier); } } else { const currentEl = this.dropdown.element.querySelector( `.${this.config.classNames.highlightedState}`, ); if (currentEl) { - nextEl = getAdjacentEl(currentEl, '[data-choice-selectable]', directionInt); + nextEl = getAdjacentEl(currentEl, selectableChoiceIdentifier, directionInt); } else { - nextEl = this.dropdown.element.querySelector('[data-choice-selectable]'); + nextEl = this.dropdown.element.querySelector(selectableChoiceIdentifier); } } @@ -1330,7 +1333,7 @@ class Choices { const onDeleteKey = () => { // If backspace or delete key is pressed and the input has no value - if (hasFocusedInput && !e.target.value && !this.isSelectOneElement) { + if (hasFocusedInput && !e.target.value && !this._isSelectOneElement) { this._handleBackspace(activeItems); e.preventDefault(); } @@ -1367,12 +1370,12 @@ class Choices { } const value = this.input.value; - const activeItems = this.store.activeItems; + const activeItems = this._store.activeItems; const canAddItem = this._canAddItem(activeItems, value); // We are typing into a text input and have a value, we want to show a dropdown // notice. Otherwise hide the dropdown - if (this.isTextElement) { + if (this._isTextElement) { if (value) { if (canAddItem.notice) { const dropdownItem = this._getTemplate('notice', canAddItem.notice); @@ -1394,9 +1397,9 @@ class Choices { // If user has removed value... if ((e.keyCode === backKey || e.keyCode === deleteKey) && !e.target.value) { // ...and it is a multiple select input, activate choices (if searching) - if (!this.isTextElement && this.isSearching) { - this.isSearching = false; - this.store.dispatch(activateChoices(true)); + if (!this._isTextElement && this._isSearching) { + this._isSearching = false; + this._store.dispatch(activateChoices(true)); } } else if (this.config.searchEnabled && canAddItem.response) { this._handleSearch(this.input.value); @@ -1412,8 +1415,8 @@ class Choices { * @private */ _onTouchMove() { - if (this.wasTap === true) { - this.wasTap = false; + if (this._wasTap === true) { + this._wasTap = false; } } @@ -1427,13 +1430,13 @@ class Choices { const target = (e.target || e.touches[0].target); // If a user tapped within our container... - if (this.wasTap === true && this.containerOuter.element.contains(target)) { + if (this._wasTap === true && this.containerOuter.element.contains(target)) { // ...and we aren't dealing with a single select box, show dropdown/focus input if ( (target === this.containerOuter.element || target === this.containerInner.element) && - !this.isSelectOneElement + !this._isSelectOneElement ) { - if (this.isTextElement) { + if (this._isTextElement) { // If text element, we only want to focus the input this.input.focus(); } else { @@ -1445,7 +1448,7 @@ class Choices { e.stopPropagation(); } - this.wasTap = true; + this._wasTap = true; } /** @@ -1459,11 +1462,11 @@ class Choices { // If we have our mouse down on the scrollbar and are on IE11... if (target === this.choiceList && isIE11()) { - this.isScrollingOnIe = true; + this._isScrollingOnIe = true; } if (this.containerOuter.element.contains(target) && target !== this.input.element) { - const activeItems = this.store.activeItems; + const activeItems = this._store.activeItems; const hasShiftKey = e.shiftKey; const buttonTarget = findAncestorByAttrName(target, 'data-button'); @@ -1509,12 +1512,12 @@ class Choices { _onClick(e) { const target = e.target; const hasActiveDropdown = this.dropdown.isActive; - const activeItems = this.store.activeItems; + const activeItems = this._store.activeItems; // If target is something that concerns us if (this.containerOuter.element.contains(target)) { if (!hasActiveDropdown) { - if (this.isTextElement) { + if (this._isTextElement) { if (document.activeElement !== this.input.element) { this.input.focus(); } @@ -1526,7 +1529,7 @@ class Choices { this.containerOuter.focus(); } } else if ( - this.isSelectOneElement && + this._isSelectOneElement && target !== this.input.element && !this.dropdown.element.contains(target) ) { @@ -1595,8 +1598,8 @@ class Choices { _onBlur(e) { const target = e.target; // If target is something that concerns us - if (this.containerOuter.element.contains(target) && !this.isScrollingOnIe) { - const activeItems = this.store.activeItems; + if (this.containerOuter.element.contains(target) && !this._isScrollingOnIe) { + const activeItems = this._store.activeItems; const hasHighlightedItems = activeItems.some(item => item.highlighted); const blurActions = { text: () => { @@ -1635,7 +1638,7 @@ class Choices { // On IE11, clicking the scollbar blurs our input and thus // closes the dropdown. To stop this, we refocus our input // if we know we are on IE *and* are scrolling. - this.isScrollingOnIe = false; + this._isScrollingOnIe = false; this.input.element.focus(); } } @@ -1727,12 +1730,12 @@ class Choices { }); if (passedEl) { - this.highlightPosition = choices.indexOf(passedEl); + this._highlightPosition = choices.indexOf(passedEl); } else { // Highlight choice based on last known highlight location - if (choices.length > this.highlightPosition) { + if (choices.length > this._highlightPosition) { // If we have an option to highlight - passedEl = choices[this.highlightPosition]; + passedEl = choices[this._highlightPosition]; } else { // Otherwise highlight the option before passedEl = choices[choices.length - 1]; @@ -1775,16 +1778,13 @@ class Choices { keyCode = null, ) { let passedValue = isType('String', value) ? value.trim() : value; + const passedKeyCode = keyCode; const passedCustomProperties = customProperties; - const items = this.store.items; + const items = this._store.items; const passedLabel = label || passedValue; const passedOptionId = parseInt(choiceId, 10) || -1; - - // Get group if group ID passed - const group = groupId >= 0 ? this.store.getGroupById(groupId) : null; - - // Generate unique id + const group = groupId >= 0 ? this._store.getGroupById(groupId) : null; const id = items ? items.length + 1 : 1; // If a prepended value has been passed, prepend it @@ -1797,7 +1797,7 @@ class Choices { passedValue += this.config.appendValue.toString(); } - this.store.dispatch( + this._store.dispatch( addItem( passedValue, passedLabel, @@ -1810,7 +1810,7 @@ class Choices { ), ); - if (this.isSelectOneElement) { + if (this._isSelectOneElement) { this.removeActiveItems(id); } @@ -1849,9 +1849,9 @@ class Choices { } const { id, value, label, choiceId, groupId } = item; - const group = groupId >= 0 ? this.store.getGroupById(groupId) : null; + const group = groupId >= 0 ? this._store.getGroupById(groupId) : null; - this.store.dispatch(removeItem(id, choiceId)); + this._store.dispatch(removeItem(id, choiceId)); if (group && group.value) { this.passedElement.triggerEvent(EVENTS.removeItem, { @@ -1897,12 +1897,12 @@ class Choices { } // Generate unique id - const choices = this.store.choices; + const choices = this._store.choices; const choiceLabel = label || value; const choiceId = choices ? choices.length + 1 : 1; - const choiceElementId = `${this.baseId}-${this.idNames.itemChoice}-${choiceId}`; + const choiceElementId = `${this._baseId}-${this.idNames.itemChoice}-${choiceId}`; - this.store.dispatch( + this._store.dispatch( addChoice( value, choiceLabel, @@ -1935,7 +1935,7 @@ class Choices { * @private */ _clearChoices() { - this.store.dispatch(clearChoices()); + this._store.dispatch(clearChoices()); } /** @@ -1955,7 +1955,7 @@ class Choices { const isDisabled = group.disabled ? group.disabled : false; if (groupChoices) { - this.store.dispatch( + this._store.dispatch( addGroup( group.label, groupId, @@ -1979,7 +1979,7 @@ class Choices { groupChoices.forEach(addGroupChoices); } else { - this.store.dispatch( + this._store.dispatch( addGroup( group.label, group.id, @@ -2029,14 +2029,14 @@ class Choices { const direction = this.passedElement.element.getAttribute('dir') || 'ltr'; const containerOuter = this._getTemplate('containerOuter', direction, - this.isSelectElement, - this.isSelectOneElement, + this._isSelectElement, + this._isSelectOneElement, this.config.searchEnabled, this.passedElement.element.type, ); const containerInner = this._getTemplate('containerInner'); - const itemList = this._getTemplate('itemList', this.isSelectOneElement); - const choiceList = this._getTemplate('choiceList', this.isSelectOneElement); + const itemList = this._getTemplate('itemList', this._isSelectOneElement); + const choiceList = this._getTemplate('choiceList', this._isSelectOneElement); const input = this._getTemplate('input'); const dropdown = this._getTemplate('dropdown'); @@ -2088,10 +2088,10 @@ class Choices { // Wrapper inner container with outer container this.containerOuter.wrap(this.containerInner.element); - if (this.isSelectOneElement) { + if (this._isSelectOneElement) { this.input.placeholder = (this.config.searchPlaceholderValue || ''); - } else if (this.placeholderValue) { - this.input.placeholder = this.placeholderValue; + } else if (this._placeholderValue) { + this.input.placeholder = this._placeholderValue; this.input.setWidth(true); } @@ -2103,19 +2103,19 @@ class Choices { this.containerOuter.element.appendChild(this.dropdown.element); this.containerInner.element.appendChild(this.itemList.element); - if (!this.isTextElement) { + if (!this._isTextElement) { this.dropdown.element.appendChild(this.choiceList.element); } - if (!this.isSelectOneElement) { + if (!this._isSelectOneElement) { this.containerInner.element.appendChild(this.input.element); } else if (this.config.searchEnabled) { this.dropdown.element.insertBefore(this.input.element, this.dropdown.element.firstChild); } - if (this.isSelectElement) { + if (this._isSelectElement) { this._addPredefinedChoices(); - } else if (this.isTextElement) { + } else if (this._isTextElement) { this._addPredefinedItems(); } } @@ -2123,8 +2123,8 @@ class Choices { _addPredefinedChoices() { const passedGroups = this.passedElement.optionGroups; - this.highlightPosition = 0; - this.isSearching = false; + this._highlightPosition = 0; + this._isSearching = false; if (passedGroups && passedGroups.length) { // If we have a placeholder option @@ -2147,7 +2147,7 @@ class Choices { } else { const passedOptions = this.passedElement.options; const filter = this.config.sortFn; - const allChoices = this.presetChoices; + const allChoices = this._presetChoices; // Create array of options from option elements passedOptions.forEach((o) => { @@ -2168,7 +2168,7 @@ class Choices { // Determine whether there is a selected choice const hasSelectedChoice = allChoices.some(choice => choice.selected); const handleChoice = (choice, index) => { - if (this.isSelectElement) { + if (this._isSelectElement) { // If the choice is actually a group if (choice.choices) { this._addGroup(choice, choice.id || null); @@ -2176,7 +2176,7 @@ class Choices { // If there is a selected choice already or the choice is not // the first in the array, add each choice normally // Otherwise pre-select the first choice in the array if it's a single select - const shouldPreselect = this.isSelectOneElement && !hasSelectedChoice && index === 0; + const shouldPreselect = this._isSelectOneElement && !hasSelectedChoice && index === 0; const isSelected = shouldPreselect ? true : choice.selected; const isDisabled = shouldPreselect ? false : choice.disabled; @@ -2228,7 +2228,7 @@ class Choices { } }; - this.presetItems.forEach(item => handlePresetItem(item)); + this._presetItems.forEach(item => handlePresetItem(item)); } _setChoiceOrItem(item) { @@ -2241,7 +2241,7 @@ class Choices { // 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 (!this.isTextElement) { + if (!this._isTextElement) { this._addChoice( item.value, item.label, @@ -2262,7 +2262,7 @@ class Choices { } }, string: () => { - if (!this.isTextElement) { + if (!this._isTextElement) { this._addChoice( item, item, @@ -2280,7 +2280,7 @@ class Choices { } _findAndSelectChoiceByValue(val) { - const choices = this.store.choices; + const choices = this._store.choices; // Check 'value' property exists and the choice isn't already selected const foundChoice = choices.find(choice => this.config.itemComparer(choice.value, val)); @@ -2317,7 +2317,7 @@ class Choices { } _generatePlaceholderValue() { - if (!this.isSelectOneElement) { + if (!this._isSelectOneElement) { return this.config.placeholder ? (this.config.placeholderValue || this.passedElement.element.getAttribute('placeholder')) : false; @@ -2328,8 +2328,8 @@ class Choices { _renderChoices() { // Get active groups/choices - const activeGroups = this.store.activeGroups; - const activeChoices = this.store.activeChoices; + const activeGroups = this._store.activeGroups; + const activeChoices = this._store.activeChoices; let choiceListFragment = document.createDocumentFragment(); @@ -2342,7 +2342,7 @@ class Choices { } // If we have grouped options - if (activeGroups.length >= 1 && !this.isSearching) { + if (activeGroups.length >= 1 && !this._isSearching) { // If we have a placeholder choice along with groups const activePlaceholders = activeChoices.filter( activeChoice => activeChoice.placeholder === true && activeChoice.groupId === -1, @@ -2361,7 +2361,7 @@ class Choices { // If we have choices to show if (choiceListFragment.childNodes && choiceListFragment.childNodes.length > 0) { - const activeItems = this.store.activeItems; + const activeItems = this._store.activeItems; const canAddItem = this._canAddItem(activeItems, this.input.value); // ...and we can select them @@ -2378,7 +2378,7 @@ class Choices { let dropdownItem; let notice; - if (this.isSearching) { + if (this._isSearching) { notice = isType('Function', this.config.noResultsText) ? this.config.noResultsText() : this.config.noResultsText; @@ -2398,7 +2398,7 @@ class Choices { _renderItems() { // Get active items (items that can be selected) - const activeItems = this.store.activeItems || []; + const activeItems = this._store.activeItems || []; // Clear list this.itemList.clear(); diff --git a/src/scripts/src/choices.test.js b/src/scripts/src/choices.test.js index ae0f10e..557a23e 100644 --- a/src/scripts/src/choices.test.js +++ b/src/scripts/src/choices.test.js @@ -60,7 +60,7 @@ describe('choices', () => { beforeEach(() => { createTemplatesSpy = spy(instance, '_createTemplates'); createInputSpy = spy(instance, '_createStructure'); - storeSubscribeSpy = spy(instance.store, 'subscribe'); + storeSubscribeSpy = spy(instance._store, 'subscribe'); renderSpy = spy(instance, 'render'); addEventListenersSpy = spy(instance, '_addEventListeners'); @@ -152,33 +152,27 @@ describe('choices', () => { }); it('removes event listeners', () => { - requestAnimationFrame; expect(removeEventListenersSpy.called).to.equal(true); }); it('reveals passed element', () => { - requestAnimationFrame; expect(passedElementRevealSpy.called).to.equal(true); }); it('reverts outer container', () => { - requestAnimationFrame; expect(containerOuterUnwrapSpy.called).to.equal(true); expect(containerOuterUnwrapSpy.lastCall.args[0]).to.equal(instance.passedElement.element); }); it('clears store', () => { - requestAnimationFrame; expect(clearStoreSpy.called).to.equal(true); }); it('nullifys templates config', () => { - requestAnimationFrame; expect(instance.config.templates).to.equal(null); }); it('resets initialise flag', () => { - requestAnimationFrame; expect(instance.initialised).to.equal(false); }); }); @@ -406,7 +400,7 @@ describe('choices', () => { describe('passing true focusInput flag with canSearch set to true', () => { beforeEach(() => { instance.dropdown.isActive = false; - instance.canSearch = true; + instance._canSearch = true; output = instance.showDropdown(true); }); @@ -497,7 +491,7 @@ describe('choices', () => { describe('passing true blurInput flag with canSearch set to true', () => { beforeEach(() => { instance.dropdown.isActive = true; - instance.canSearch = true; + instance._canSearch = true; output = instance.hideDropdown(true); }); @@ -573,15 +567,15 @@ describe('choices', () => { storeGetGroupByIdStub = stub().returns({ value: groupIdValue, }); - storeDispatchSpy = spy(instance.store, 'dispatch'); + storeDispatchSpy = spy(instance._store, 'dispatch'); - instance.store.getGroupById = storeGetGroupByIdStub; + instance._store.getGroupById = storeGetGroupByIdStub; instance.passedElement.triggerEvent = passedElementTriggerEventStub; }); afterEach(() => { storeDispatchSpy.restore(); - instance.store.getGroupById.reset(); + instance._store.getGroupById.reset(); instance.passedElement.triggerEvent.reset(); }); @@ -684,15 +678,15 @@ describe('choices', () => { storeGetGroupByIdStub = stub().returns({ value: groupIdValue, }); - storeDispatchSpy = spy(instance.store, 'dispatch'); + storeDispatchSpy = spy(instance._store, 'dispatch'); - instance.store.getGroupById = storeGetGroupByIdStub; + instance._store.getGroupById = storeGetGroupByIdStub; instance.passedElement.triggerEvent = passedElementTriggerEventStub; }); afterEach(() => { storeDispatchSpy.restore(); - instance.store.getGroupById.reset(); + instance._store.getGroupById.reset(); instance.passedElement.triggerEvent.reset(); }); @@ -800,7 +794,7 @@ describe('choices', () => { ]; beforeEach(() => { - storeGetItemsStub = stub(instance.store, 'items').get(() => items); + storeGetItemsStub = stub(instance._store, 'items').get(() => items); highlightItemStub = stub(); instance.highlightItem = highlightItemStub; @@ -838,7 +832,7 @@ describe('choices', () => { ]; beforeEach(() => { - storeGetItemsStub = stub(instance.store, 'items').get(() => items); + storeGetItemsStub = stub(instance._store, 'items').get(() => items); unhighlightItemStub = stub(); instance.unhighlightItem = unhighlightItemStub; @@ -865,13 +859,13 @@ describe('choices', () => { beforeEach(() => { storeDispatchStub = stub(); - instance.store.dispatch = storeDispatchStub; + instance._store.dispatch = storeDispatchStub; output = instance.clearStore(); }); afterEach(() => { - instance.store.dispatch.reset(); + instance._store.dispatch.reset(); }); returnsInstance(output); @@ -890,21 +884,21 @@ describe('choices', () => { beforeEach(() => { inputClearSpy = spy(instance.input, 'clear'); storeDispatchStub = stub(); - instance.store.dispatch = storeDispatchStub; + instance._store.dispatch = storeDispatchStub; output = instance.clearInput(); }); afterEach(() => { inputClearSpy.restore(); - instance.store.dispatch.reset(); + instance._store.dispatch.reset(); }); returnsInstance(output); describe('text element', () => { beforeEach(() => { - instance.isSelectOneElement = false; - instance.isTextElement = false; + instance._isSelectOneElement = false; + instance._isTextElement = false; output = instance.clearInput(); }); @@ -917,8 +911,8 @@ describe('choices', () => { describe('select element with search enabled', () => { beforeEach(() => { - instance.isSelectOneElement = true; - instance.isTextElement = false; + instance._isSelectOneElement = true; + instance._isTextElement = false; instance.config.searchEnabled = true; output = instance.clearInput(); @@ -930,7 +924,7 @@ describe('choices', () => { }); it('resets search flag', () => { - expect(instance.isSearching).to.equal(false); + expect(instance._isSearching).to.equal(false); }); it('dispatches activateChoices action', () => { @@ -981,7 +975,7 @@ describe('choices', () => { describe('text element', () => { beforeEach(() => { - instance.isSelectElement = false; + instance._isSelectElement = false; output = instance.ajax(() => {}); }); @@ -1003,7 +997,7 @@ describe('choices', () => { beforeEach(() => { instance.initialised = true; - instance.isSelectElement = true; + instance._isSelectElement = true; ajaxCallbackStub = stub(); callback = stub(); output = instance.ajax(callback); @@ -1100,7 +1094,7 @@ describe('choices', () => { describe('when already initialised and not text element', () => { beforeEach(() => { instance.initialised = true; - instance.isTextElement = false; + instance._isTextElement = false; }); describe('passing a string value', () => { @@ -1153,7 +1147,7 @@ describe('choices', () => { ]; beforeEach(() => { - activeItemsStub = stub(instance.store, 'activeItems').get(() => items); + activeItemsStub = stub(instance._store, 'activeItems').get(() => items); }); afterEach(() => { @@ -1163,7 +1157,7 @@ describe('choices', () => { describe('passing true valueOnly flag', () => { describe('select one input', () => { beforeEach(() => { - instance.isSelectOneElement = true; + instance._isSelectOneElement = true; output = instance.getValue(true); }); @@ -1174,7 +1168,7 @@ describe('choices', () => { describe('non select one input', () => { beforeEach(() => { - instance.isSelectOneElement = false; + instance._isSelectOneElement = false; output = instance.getValue(true); }); @@ -1187,7 +1181,7 @@ describe('choices', () => { describe('passing false valueOnly flag', () => { describe('select one input', () => { beforeEach(() => { - instance.isSelectOneElement = true; + instance._isSelectOneElement = true; output = instance.getValue(false); }); @@ -1198,7 +1192,7 @@ describe('choices', () => { describe('non select one input', () => { beforeEach(() => { - instance.isSelectOneElement = false; + instance._isSelectOneElement = false; output = instance.getValue(false); }); @@ -1239,7 +1233,7 @@ describe('choices', () => { beforeEach(() => { removeItemStub = stub(); - activeItemsStub = stub(instance.store, 'activeItems').get(() => items); + activeItemsStub = stub(instance._store, 'activeItems').get(() => items); instance._removeItem = removeItemStub; output = instance.removeActiveItemsByValue(value); @@ -1278,7 +1272,7 @@ describe('choices', () => { beforeEach(() => { removeItemStub = stub(); - activeItemsStub = stub(instance.store, 'activeItems').get(() => items); + activeItemsStub = stub(instance._store, 'activeItems').get(() => items); instance._removeItem = removeItemStub; }); @@ -1333,7 +1327,7 @@ describe('choices', () => { beforeEach(() => { - highlightedActiveItemsStub = stub(instance.store, 'highlightedActiveItems').get(() => items); + highlightedActiveItemsStub = stub(instance._store, 'highlightedActiveItems').get(() => items); removeItemStub = stub(); triggerChangeStub = stub(); @@ -1430,7 +1424,7 @@ describe('choices', () => { describe('when element is not select element', () => { beforeEach(() => { - instance.isSelectElement = false; + instance._isSelectElement = false; instance.setChoices(choices, value, label, false); }); @@ -1440,7 +1434,7 @@ describe('choices', () => { describe('passing invalid arguments', () => { describe('passing an empty array', () => { beforeEach(() => { - instance.isSelectElement = true; + instance._isSelectElement = true; instance.setChoices([], value, label, false); }); @@ -1449,7 +1443,7 @@ describe('choices', () => { describe('passing no value', () => { beforeEach(() => { - instance.isSelectElement = true; + instance._isSelectElement = true; instance.setChoices(choices, undefined, 'label', false); }); @@ -1459,7 +1453,7 @@ describe('choices', () => { describe('passing valid arguments', () => { beforeEach(() => { - instance.isSelectElement = true; + instance._isSelectElement = true; }); it('removes loading state', () => { @@ -1629,7 +1623,7 @@ describe('choices', () => { describe('select-one element', () => { beforeEach(() => { - instance.isSelectOneElement = true; + instance._isSelectOneElement = true; }); it('calls _createChoicesFragment with choices that belong to each group', () => { @@ -1667,7 +1661,7 @@ describe('choices', () => { describe('text/select-multiple element', () => { describe('renderSelectedChoices set to "always"', () => { beforeEach(() => { - instance.isSelectOneElement = false; + instance._isSelectOneElement = false; instance.config.renderSelectedChoices = 'always'; }); @@ -1705,7 +1699,7 @@ describe('choices', () => { describe('renderSelectedChoices not set to "always"', () => { beforeEach(() => { - instance.isSelectOneElement = false; + instance._isSelectOneElement = false; instance.config.renderSelectedChoices = false; }); @@ -1737,31 +1731,31 @@ describe('choices', () => { }); }); - describe('render', () => { - beforeEach(() => {}); + // describe('render', () => { + // beforeEach(() => {}); - describe('no change to state', () => { - it('returns early', () => {}); - }); + // describe('no change to state', () => { + // it('returns early', () => {}); + // }); - describe('change to state', () => { - it('updates previous state to current state', () => {}); + // describe('change to state', () => { + // it('updates previous state to current state', () => {}); - describe('select element', () => { - it('clears choice list', () => {}); + // describe('select element', () => { + // it('clears choice list', () => {}); - describe('when resetScrollPosition config option is set to true', () => { - it('scrolls to top of choice list', () => {}); - }); - }); + // describe('when resetScrollPosition config option is set to true', () => { + // it('scrolls to top of choice list', () => {}); + // }); + // }); - describe('text element', () => { - describe('active items in store', () => { - it('clears item list', () => {}); - it('renders active items inside item list', () => {}); - }); - }); - }); - }); + // describe('text element', () => { + // describe('active items in store', () => { + // it('clears item list', () => {}); + // it('renders active items inside item list', () => {}); + // }); + // }); + // }); + // }); }); }); diff --git a/src/scripts/src/components/input.js b/src/scripts/src/components/input.js index 4e98599..8ab3a3e 100644 --- a/src/scripts/src/components/input.js +++ b/src/scripts/src/components/input.js @@ -119,12 +119,12 @@ export default class Input { * @return */ setWidth(enforceWidth) { - if (this.placeholderValue) { + if (this._placeholderValue) { // If there is a placeholder, we only want to set the width of the input when it is a greater // length than 75% of the placeholder. This stops the input jumping around. if ( (this.element.value && - this.element.value.length >= (this.placeholderValue.length / 1.25)) || + this.element.value.length >= (this._placeholderValue.length / 1.25)) || enforceWidth ) { this.element.style.width = this.calcWidth(); diff --git a/src/scripts/src/components/input.test.js b/src/scripts/src/components/input.test.js index e5957c9..57b8e98 100644 --- a/src/scripts/src/components/input.test.js +++ b/src/scripts/src/components/input.test.js @@ -281,7 +281,7 @@ describe('components/input', () => { describe('with a placeholder', () => { describe('when value length is greater or equal to 75% of the placeholder length', () => { it('sets the width of the element based on input value', () => { - instance.placeholderValue = 'This is a test'; + instance._placeholderValue = 'This is a test'; instance.element.value = 'This is a test'; expect(instance.element.style.width).to.not.equal(inputWidth); instance.setWidth(); @@ -292,7 +292,7 @@ describe('components/input', () => { describe('when width is enforced', () => { it('sets the width of the element based on input value', () => { - instance.placeholderValue = 'This is a test'; + instance._placeholderValue = 'This is a test'; instance.element.value = ''; expect(instance.element.style.width).to.not.equal(inputWidth); instance.setWidth(true); @@ -303,7 +303,7 @@ describe('components/input', () => { describe('when value length is less than 75% of the placeholder length', () => { it('does not set the width of the element', () => { - instance.placeholderValue = 'This is a test'; + instance._placeholderValue = 'This is a test'; instance.element.value = 'Test'; instance.setWidth(); expect(calcWidthStub.callCount).to.equal(0); diff --git a/src/scripts/src/store/store.js b/src/scripts/src/store/store.js index 5dd4693..2ddf594 100644 --- a/src/scripts/src/store/store.js +++ b/src/scripts/src/store/store.js @@ -3,7 +3,7 @@ import rootReducer from './../reducers/index'; export default class Store { constructor() { - this.store = createStore( + this._store = createStore( rootReducer, window.devToolsExtension ? window.devToolsExtension() : @@ -17,7 +17,7 @@ export default class Store { * @return */ subscribe(onChange) { - this.store.subscribe(onChange); + this._store.subscribe(onChange); } /** @@ -26,7 +26,7 @@ export default class Store { * @return */ dispatch(action) { - this.store.dispatch(action); + this._store.dispatch(action); } /** @@ -34,7 +34,7 @@ export default class Store { * @return {Object} State */ get state() { - return this.store.getState(); + return this._store.getState(); } /** diff --git a/src/scripts/src/store/store.test.js b/src/scripts/src/store/store.test.js index 0ac2651..f9141a4 100644 --- a/src/scripts/src/store/store.test.js +++ b/src/scripts/src/store/store.test.js @@ -10,9 +10,9 @@ describe('reducers/store', () => { beforeEach(() => { instance = new Store(); - subscribeStub = sinon.stub(instance.store, 'subscribe'); - dispatchStub = sinon.stub(instance.store, 'dispatch'); - getStateStub = sinon.stub(instance.store, 'getState'); + subscribeStub = sinon.stub(instance._store, 'subscribe'); + dispatchStub = sinon.stub(instance._store, 'dispatch'); + getStateStub = sinon.stub(instance._store, 'getState'); }); afterEach(() => { @@ -24,7 +24,7 @@ describe('reducers/store', () => { describe('constructor', () => { it('creates redux store', () => { - expect(instance.store).to.contain.keys([ + expect(instance._store).to.contain.keys([ 'subscribe', 'dispatch', 'getState',