diff --git a/README.md b/README.md
index 3f92d8ae2..3df59d5ca 100644
--- a/README.md
+++ b/README.md
@@ -32,11 +32,11 @@ Or include Choices directly:
```html
-
+
-
+
-
+
```
## Setup
diff --git a/src/scripts/choices.js b/src/scripts/choices.js
index 13f80bec8..05a4e4e9f 100644
--- a/src/scripts/choices.js
+++ b/src/scripts/choices.js
@@ -131,7 +131,8 @@ class Choices {
this.passedElement.value.split(this.config.delimiter),
);
}
- this.render = this.render.bind(this);
+
+ this._render = this._render.bind(this);
this._onFocus = this._onFocus.bind(this);
this._onBlur = this._onBlur.bind(this);
this._onKeyUp = this._onKeyUp.bind(this);
@@ -169,8 +170,8 @@ class Choices {
// Set initial state (We need to clone the state because some reducers
// modify the inner objects properties in the state) 🤢
this._initialState = cloneObject(this._store.state);
- this._store.subscribe(this.render);
- this.render();
+ this._store.subscribe(this._render);
+ this._render();
this._addEventListeners();
this.initialised = true;
@@ -232,32 +233,6 @@ class Choices {
return this;
}
- render() {
- 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;
- const shouldRenderChoices = this._isSelectElement;
- const shouldRenderItems =
- this._currentState.items !== this._prevState.items;
-
- if (!stateChanged) {
- return;
- }
-
- if (shouldRenderChoices) {
- this._renderChoices();
- }
-
- if (shouldRenderItems) {
- this._renderItems();
- }
-
- this._prevState = this._currentState;
- }
-
highlightItem(item, runEvent = true) {
if (!item) {
return this;
@@ -485,6 +460,123 @@ class Choices {
= Private functions =
============================================= */
+ _render() {
+ 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;
+ const shouldRenderChoices = this._isSelectElement;
+ const shouldRenderItems =
+ this._currentState.items !== this._prevState.items;
+
+ if (!stateChanged) {
+ return;
+ }
+
+ if (shouldRenderChoices) {
+ this._renderChoices();
+ }
+
+ if (shouldRenderItems) {
+ this._renderItems();
+ }
+
+ this._prevState = this._currentState;
+ }
+
+ _renderChoices() {
+ const { activeGroups, activeChoices } = this._store;
+ let choiceListFragment = document.createDocumentFragment();
+
+ this.choiceList.clear();
+
+ if (this.config.resetScrollPosition) {
+ requestAnimationFrame(() => this.choiceList.scrollToTop());
+ }
+
+ // If we have grouped options
+ 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,
+ );
+ if (activePlaceholders.length >= 1) {
+ choiceListFragment = this._createChoicesFragment(
+ activePlaceholders,
+ choiceListFragment,
+ );
+ }
+ choiceListFragment = this._createGroupsFragment(
+ activeGroups,
+ activeChoices,
+ choiceListFragment,
+ );
+ } else if (activeChoices.length >= 1) {
+ choiceListFragment = this._createChoicesFragment(
+ activeChoices,
+ choiceListFragment,
+ );
+ }
+
+ // If we have choices to show
+ if (
+ choiceListFragment.childNodes &&
+ choiceListFragment.childNodes.length > 0
+ ) {
+ const activeItems = this._store.activeItems;
+ const canAddItem = this._canAddItem(activeItems, this.input.value);
+
+ // ...and we can select them
+ if (canAddItem.response) {
+ // ...append them and highlight the first choice
+ this.choiceList.append(choiceListFragment);
+ this._highlightChoice();
+ } else {
+ // ...otherwise show a notice
+ this.choiceList.append(this._getTemplate('notice', canAddItem.notice));
+ }
+ } else {
+ // Otherwise show a notice
+ let dropdownItem;
+ let notice;
+
+ if (this._isSearching) {
+ notice = isType('Function', this.config.noResultsText)
+ ? this.config.noResultsText()
+ : this.config.noResultsText;
+
+ dropdownItem = this._getTemplate('notice', notice, 'no-results');
+ } else {
+ notice = isType('Function', this.config.noChoicesText)
+ ? this.config.noChoicesText()
+ : this.config.noChoicesText;
+
+ dropdownItem = this._getTemplate('notice', notice, 'no-choices');
+ }
+
+ this.choiceList.append(dropdownItem);
+ }
+ }
+
+ _renderItems() {
+ const activeItems = this._store.activeItems || [];
+ this.itemList.clear();
+
+ if (activeItems.length) {
+ // Create a fragment to store our list items
+ // (so we don't have to update the DOM for each item)
+ const itemListFragment = this._createItemsFragment(activeItems);
+
+ // If we have items to add, append them
+ if (itemListFragment.childNodes) {
+ this.itemList.append(itemListFragment);
+ }
+ }
+ }
+
_createGroupsFragment(groups, choices, fragment) {
const groupFragment = fragment || document.createDocumentFragment();
const getGroupChoices = group =>
@@ -793,6 +885,30 @@ class Choices {
}
}
+ _handleSearch(value) {
+ if (!value || !this.input.isFocussed) {
+ return;
+ }
+
+ 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 >= searchFloor) {
+ const resultCount = searchChoices ? this._searchChoices(value) : 0;
+ // Trigger search event
+ this.passedElement.triggerEvent(EVENTS.search, {
+ value,
+ resultCount,
+ });
+ } else if (hasUnactiveChoices) {
+ // Otherwise reset choices to active
+ this._isSearching = false;
+ this._store.dispatch(activateChoices(true));
+ }
+ }
+
_canAddItem(activeItems, value) {
let canAddItem = true;
let notice = isType('Function', this.config.addItemText)
@@ -916,30 +1032,6 @@ class Choices {
return results.length;
}
- _handleSearch(value) {
- if (!value || !this.input.isFocussed) {
- return;
- }
-
- 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 >= searchFloor) {
- const resultCount = searchChoices ? this._searchChoices(value) : 0;
- // Trigger search event
- this.passedElement.triggerEvent(EVENTS.search, {
- value,
- resultCount,
- });
- } else if (hasUnactiveChoices) {
- // Otherwise reset choices to active
- this._isSearching = false;
- this._store.dispatch(activateChoices(true));
- }
- }
-
_addEventListeners() {
document.addEventListener('keyup', this._onKeyUp);
document.addEventListener('keydown', this._onKeyDown);
@@ -1960,97 +2052,6 @@ class Choices {
: false;
}
- _renderChoices() {
- const { activeGroups, activeChoices } = this._store;
- let choiceListFragment = document.createDocumentFragment();
-
- this.choiceList.clear();
-
- if (this.config.resetScrollPosition) {
- requestAnimationFrame(() => this.choiceList.scrollToTop());
- }
-
- // If we have grouped options
- 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,
- );
- if (activePlaceholders.length >= 1) {
- choiceListFragment = this._createChoicesFragment(
- activePlaceholders,
- choiceListFragment,
- );
- }
- choiceListFragment = this._createGroupsFragment(
- activeGroups,
- activeChoices,
- choiceListFragment,
- );
- } else if (activeChoices.length >= 1) {
- choiceListFragment = this._createChoicesFragment(
- activeChoices,
- choiceListFragment,
- );
- }
-
- // If we have choices to show
- if (
- choiceListFragment.childNodes &&
- choiceListFragment.childNodes.length > 0
- ) {
- const activeItems = this._store.activeItems;
- const canAddItem = this._canAddItem(activeItems, this.input.value);
-
- // ...and we can select them
- if (canAddItem.response) {
- // ...append them and highlight the first choice
- this.choiceList.append(choiceListFragment);
- this._highlightChoice();
- } else {
- // ...otherwise show a notice
- this.choiceList.append(this._getTemplate('notice', canAddItem.notice));
- }
- } else {
- // Otherwise show a notice
- let dropdownItem;
- let notice;
-
- if (this._isSearching) {
- notice = isType('Function', this.config.noResultsText)
- ? this.config.noResultsText()
- : this.config.noResultsText;
-
- dropdownItem = this._getTemplate('notice', notice, 'no-results');
- } else {
- notice = isType('Function', this.config.noChoicesText)
- ? this.config.noChoicesText()
- : this.config.noChoicesText;
-
- dropdownItem = this._getTemplate('notice', notice, 'no-choices');
- }
-
- this.choiceList.append(dropdownItem);
- }
- }
-
- _renderItems() {
- const activeItems = this._store.activeItems || [];
- this.itemList.clear();
-
- if (activeItems.length) {
- // Create a fragment to store our list items
- // (so we don't have to update the DOM for each item)
- const itemListFragment = this._createItemsFragment(activeItems);
-
- // If we have items to add, append them
- if (itemListFragment.childNodes) {
- this.itemList.append(itemListFragment);
- }
- }
- }
-
/* ===== End of Private functions ====== */
}
diff --git a/src/scripts/choices.test.js b/src/scripts/choices.test.js
index 4999c2cf7..800e83d61 100644
--- a/src/scripts/choices.test.js
+++ b/src/scripts/choices.test.js
@@ -61,7 +61,7 @@ describe('choices', () => {
createTemplatesSpy = spy(instance, '_createTemplates');
createInputSpy = spy(instance, '_createStructure');
storeSubscribeSpy = spy(instance._store, 'subscribe');
- renderSpy = spy(instance, 'render');
+ renderSpy = spy(instance, '_render');
addEventListenersSpy = spy(instance, '_addEventListeners');
instance.initialised = false;
@@ -90,7 +90,7 @@ describe('choices', () => {
it('subscribes to store with render method', () => {
expect(storeSubscribeSpy.called).to.equal(true);
- expect(storeSubscribeSpy.lastCall.args[0]).to.equal(instance.render);
+ expect(storeSubscribeSpy.lastCall.args[0]).to.equal(instance._render);
});
it('fires initial render', () => {
@@ -1748,32 +1748,5 @@ describe('choices', () => {
});
});
});
-
- // describe('render', () => {
- // beforeEach(() => {});
-
- // describe('no change to state', () => {
- // it('returns early', () => {});
- // });
-
- // describe('change to state', () => {
- // it('updates previous state to current state', () => {});
-
- // describe('select element', () => {
- // it('clears 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', () => {});
- // });
- // });
- // });
- // });
});
});