diff --git a/src/scripts/actions/general.js b/src/scripts/actions/general.js new file mode 100644 index 0000000..1cf6012 --- /dev/null +++ b/src/scripts/actions/general.js @@ -0,0 +1,6 @@ +/* eslint-disable import/prefer-default-export */ + +export const setIsLoading = isLoading => ({ + type: 'LOADING', + isLoading, +}); diff --git a/src/scripts/actions/general.test.js b/src/scripts/actions/general.test.js new file mode 100644 index 0000000..90db428 --- /dev/null +++ b/src/scripts/actions/general.test.js @@ -0,0 +1,15 @@ +import { expect } from 'chai'; +import * as actions from './general'; + +describe('actions/general', () => { + describe('setIsLoading action', () => { + it('returns LOADING action with passed loading flag', () => { + const expectedAction = { + type: 'LOADING', + isLoading: true, + }; + + expect(actions.setIsLoading(true)).to.eql(expectedAction); + }); + }); +}); diff --git a/src/scripts/choices.js b/src/scripts/choices.js index 3955777..9f3ddc9 100644 --- a/src/scripts/choices.js +++ b/src/scripts/choices.js @@ -22,6 +22,7 @@ import { import { addItem, removeItem, highlightItem } from './actions/items'; import { addGroup } from './actions/groups'; import { clearAll, resetTo } from './actions/misc'; +import { setIsLoading } from './actions/general'; import { isScrolledIntoView, getAdjacentEl, @@ -438,7 +439,9 @@ class Choices { } }; + this._setLoading(true); choices.forEach(addGroupsAndChoices); + this._setLoading(false); return this; } @@ -478,6 +481,10 @@ class Choices { ============================================= */ _render() { + if (this._store.isLoading()) { + return; + } + this._currentState = this._store.state; const stateChanged = @@ -870,12 +877,16 @@ class Choices { } } - _handleLoadingState(isLoading = true) { + _setLoading(isLoading) { + this._store.dispatch(setIsLoading(isLoading)); + } + + _handleLoadingState(setLoading = true) { let placeholderItem = this.itemList.getChild( `.${this.config.classNames.placeholder}`, ); - if (isLoading) { + if (setLoading) { this.disable(); this.containerOuter.addLoadingState(); @@ -994,6 +1005,7 @@ class Choices { ) { // Remove loading states/text this._handleLoadingState(false); + this._setLoading(true); // Add each result as a choice parsedResults.forEach(result => { if (result.choices) { @@ -1015,6 +1027,8 @@ class Choices { } }); + this._setLoading(false); + if (this._isSelectOneElement) { this._selectPlaceholderChoice(); } @@ -1872,6 +1886,7 @@ class Choices { this._highlightPosition = 0; this._isSearching = false; + this._setLoading(true); if (passedGroups && passedGroups.length) { // If we have a placeholder option @@ -1961,6 +1976,8 @@ class Choices { // Add each choice allChoices.forEach((choice, index) => handleChoice(choice, index)); } + + this._setLoading(false); } _addPredefinedItems() { diff --git a/src/scripts/reducers/general.js b/src/scripts/reducers/general.js new file mode 100644 index 0000000..bf6ad5d --- /dev/null +++ b/src/scripts/reducers/general.js @@ -0,0 +1,19 @@ +export const defaultState = { + loading: false, +}; + +const general = (state = defaultState, action) => { + switch (action.type) { + case 'LOADING': { + return { + loading: action.isLoading, + }; + } + + default: { + return state; + } + } +}; + +export default general; diff --git a/src/scripts/reducers/general.test.js b/src/scripts/reducers/general.test.js new file mode 100644 index 0000000..028f843 --- /dev/null +++ b/src/scripts/reducers/general.test.js @@ -0,0 +1,23 @@ +import { expect } from 'chai'; +import general, { defaultState } from './general'; + +describe('reducers/general', () => { + it('should return same state when no action matches', () => { + expect(general(defaultState, {})).to.equal(defaultState); + }); + + describe('LOADING', () => { + it('sets loading state', () => { + const expectedState = { + loading: true, + }; + + const actualState = general(undefined, { + type: 'LOADING', + isLoading: true, + }); + + expect(expectedState).to.eql(actualState); + }); + }); +}); diff --git a/src/scripts/reducers/index.js b/src/scripts/reducers/index.js index 6929262..31dc7b3 100644 --- a/src/scripts/reducers/index.js +++ b/src/scripts/reducers/index.js @@ -2,12 +2,14 @@ import { combineReducers } from 'redux'; import items from './items'; import groups from './groups'; import choices from './choices'; +import general from './general'; import { cloneObject } from '../lib/utils'; const appReducer = combineReducers({ items, groups, choices, + general, }); const rootReducer = (passedState, action) => { diff --git a/src/scripts/reducers/index.test.js b/src/scripts/reducers/index.test.js index 79c2028..c662cc8 100644 --- a/src/scripts/reducers/index.test.js +++ b/src/scripts/reducers/index.test.js @@ -33,6 +33,9 @@ describe('reducers/rootReducer', () => { items: [], groups: [], choices: [], + general: { + loading: false, + }, }); }); }); diff --git a/src/scripts/store/store.js b/src/scripts/store/store.js index 32d3b35..5c819d7 100644 --- a/src/scripts/store/store.js +++ b/src/scripts/store/store.js @@ -129,6 +129,14 @@ export default class Store { }, []); } + /** + * Get loading state from store + * @return {Boolean} Loading State + */ + isLoading() { + return this.state.general.loading; + } + /** * Get single choice by it's ID * @return {Object} Found choice