mirror of
https://github.com/Choices-js/Choices.git
synced 2024-06-19 22:25:09 +02:00
Fix remaning type issues
This commit is contained in:
parent
44666936a9
commit
a80d42f5f6
|
@ -14,9 +14,14 @@ export interface AddChoiceAction {
|
||||||
keyCode: number;
|
keyCode: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface Result<T> {
|
||||||
|
item: T;
|
||||||
|
score: number;
|
||||||
|
}
|
||||||
|
|
||||||
export interface FilterChoicesAction {
|
export interface FilterChoicesAction {
|
||||||
type: typeof ACTION_TYPES.FILTER_CHOICES;
|
type: typeof ACTION_TYPES.FILTER_CHOICES;
|
||||||
results: Choice[];
|
results: Result<Choice>[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ActivateChoicesAction {
|
export interface ActivateChoicesAction {
|
||||||
|
@ -51,7 +56,9 @@ export const addChoice = ({
|
||||||
keyCode,
|
keyCode,
|
||||||
});
|
});
|
||||||
|
|
||||||
export const filterChoices = (results: Choice[]): FilterChoicesAction => ({
|
export const filterChoices = (
|
||||||
|
results: Result<Choice>[],
|
||||||
|
): FilterChoicesAction => ({
|
||||||
type: ACTION_TYPES.FILTER_CHOICES,
|
type: ACTION_TYPES.FILTER_CHOICES,
|
||||||
results,
|
results,
|
||||||
});
|
});
|
||||||
|
|
|
@ -5,7 +5,7 @@ describe('actions/groups', () => {
|
||||||
describe('addGroup action', () => {
|
describe('addGroup action', () => {
|
||||||
it('returns ADD_GROUP action', () => {
|
it('returns ADD_GROUP action', () => {
|
||||||
const value = 'test';
|
const value = 'test';
|
||||||
const id = 'test';
|
const id = 1;
|
||||||
const active = true;
|
const active = true;
|
||||||
const disabled = false;
|
const disabled = false;
|
||||||
const expectedAction = {
|
const expectedAction = {
|
||||||
|
|
|
@ -6,9 +6,9 @@ describe('actions/items', () => {
|
||||||
it('returns ADD_ITEM action', () => {
|
it('returns ADD_ITEM action', () => {
|
||||||
const value = 'test';
|
const value = 'test';
|
||||||
const label = 'test';
|
const label = 'test';
|
||||||
const id = '1234';
|
const id = 1;
|
||||||
const choiceId = '1234';
|
const choiceId = 1;
|
||||||
const groupId = 'test';
|
const groupId = 1;
|
||||||
const customProperties = { test: true };
|
const customProperties = { test: true };
|
||||||
const placeholder = true;
|
const placeholder = true;
|
||||||
const keyCode = 10;
|
const keyCode = 10;
|
||||||
|
@ -42,8 +42,8 @@ describe('actions/items', () => {
|
||||||
|
|
||||||
describe('removeItem action', () => {
|
describe('removeItem action', () => {
|
||||||
it('returns REMOVE_ITEM action', () => {
|
it('returns REMOVE_ITEM action', () => {
|
||||||
const id = '1234';
|
const id = 1;
|
||||||
const choiceId = '1';
|
const choiceId = 1;
|
||||||
const expectedAction = {
|
const expectedAction = {
|
||||||
type: 'REMOVE_ITEM',
|
type: 'REMOVE_ITEM',
|
||||||
id,
|
id,
|
||||||
|
@ -56,7 +56,7 @@ describe('actions/items', () => {
|
||||||
|
|
||||||
describe('highlightItem action', () => {
|
describe('highlightItem action', () => {
|
||||||
it('returns HIGHLIGHT_ITEM action', () => {
|
it('returns HIGHLIGHT_ITEM action', () => {
|
||||||
const id = '1234';
|
const id = 1;
|
||||||
const highlighted = true;
|
const highlighted = true;
|
||||||
|
|
||||||
const expectedAction = {
|
const expectedAction = {
|
||||||
|
|
|
@ -14,7 +14,14 @@ describe('actions/misc', () => {
|
||||||
|
|
||||||
describe('resetTo action', () => {
|
describe('resetTo action', () => {
|
||||||
it('returns RESET_TO action', () => {
|
it('returns RESET_TO action', () => {
|
||||||
const state = { test: true };
|
const state = {
|
||||||
|
choices: [],
|
||||||
|
items: [],
|
||||||
|
groups: [],
|
||||||
|
general: {
|
||||||
|
loading: false,
|
||||||
|
},
|
||||||
|
};
|
||||||
const expectedAction = {
|
const expectedAction = {
|
||||||
type: 'RESET_TO',
|
type: 'RESET_TO',
|
||||||
state,
|
state,
|
||||||
|
|
|
@ -3,9 +3,11 @@ import { spy, stub } from 'sinon';
|
||||||
import sinonChai from 'sinon-chai';
|
import sinonChai from 'sinon-chai';
|
||||||
|
|
||||||
import Choices from './choices';
|
import Choices from './choices';
|
||||||
|
|
||||||
import { EVENTS, ACTION_TYPES, DEFAULT_CONFIG, KEY_CODES } from './constants';
|
import { EVENTS, ACTION_TYPES, DEFAULT_CONFIG, KEY_CODES } from './constants';
|
||||||
import { WrappedSelect, WrappedInput } from './components/index';
|
import { WrappedSelect, WrappedInput } from './components/index';
|
||||||
import { removeItem } from './actions/items';
|
import { removeItem } from './actions/items';
|
||||||
|
import { Item, Choice, Group } from './interfaces';
|
||||||
|
|
||||||
chai.use(sinonChai);
|
chai.use(sinonChai);
|
||||||
|
|
||||||
|
@ -28,12 +30,6 @@ describe('choices', () => {
|
||||||
instance = null;
|
instance = null;
|
||||||
});
|
});
|
||||||
|
|
||||||
const returnsInstance = () => {
|
|
||||||
it('returns this', () => {
|
|
||||||
expect(output).to.eql(instance);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
describe('constructor', () => {
|
describe('constructor', () => {
|
||||||
describe('config', () => {
|
describe('config', () => {
|
||||||
describe('not passing config options', () => {
|
describe('not passing config options', () => {
|
||||||
|
@ -88,7 +84,7 @@ describe('choices', () => {
|
||||||
`;
|
`;
|
||||||
|
|
||||||
instance = new Choices('[data-choice]', {
|
instance = new Choices('[data-choice]', {
|
||||||
renderSelectedChoices: 'test',
|
renderSelectedChoices: 'test' as any,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(instance.config.renderSelectedChoices).to.equal('auto');
|
expect(instance.config.renderSelectedChoices).to.equal('auto');
|
||||||
|
@ -211,7 +207,7 @@ describe('choices', () => {
|
||||||
<input data-choice type="text" id="input-1" />
|
<input data-choice type="text" id="input-1" />
|
||||||
`;
|
`;
|
||||||
|
|
||||||
instance = new Choices(document.querySelector('[data-choice]'));
|
instance = new Choices('[data-choice]');
|
||||||
|
|
||||||
expect(instance.passedElement).to.be.an.instanceOf(WrappedInput);
|
expect(instance.passedElement).to.be.an.instanceOf(WrappedInput);
|
||||||
});
|
});
|
||||||
|
@ -223,7 +219,7 @@ describe('choices', () => {
|
||||||
<select data-choice id="select-1"></select>
|
<select data-choice id="select-1"></select>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
instance = new Choices(document.querySelector('[data-choice]'));
|
instance = new Choices('[data-choice]');
|
||||||
|
|
||||||
expect(instance.passedElement).to.be.an.instanceOf(WrappedSelect);
|
expect(instance.passedElement).to.be.an.instanceOf(WrappedSelect);
|
||||||
});
|
});
|
||||||
|
@ -423,7 +419,9 @@ describe('choices', () => {
|
||||||
output = instance.enable();
|
output = instance.enable();
|
||||||
});
|
});
|
||||||
|
|
||||||
returnsInstance(output);
|
it('returns this', () => {
|
||||||
|
expect(output).to.eql(instance);
|
||||||
|
});
|
||||||
|
|
||||||
it('returns early', () => {
|
it('returns early', () => {
|
||||||
expect(passedElementEnableSpy.called).to.equal(false);
|
expect(passedElementEnableSpy.called).to.equal(false);
|
||||||
|
@ -481,7 +479,9 @@ describe('choices', () => {
|
||||||
output = instance.disable();
|
output = instance.disable();
|
||||||
});
|
});
|
||||||
|
|
||||||
returnsInstance(output);
|
it('returns this', () => {
|
||||||
|
expect(output).to.eql(instance);
|
||||||
|
});
|
||||||
|
|
||||||
it('returns early', () => {
|
it('returns early', () => {
|
||||||
expect(removeEventListenersSpy.called).to.equal(false);
|
expect(removeEventListenersSpy.called).to.equal(false);
|
||||||
|
@ -638,7 +638,9 @@ describe('choices', () => {
|
||||||
output = instance.hideDropdown();
|
output = instance.hideDropdown();
|
||||||
});
|
});
|
||||||
|
|
||||||
returnsInstance(output);
|
it('returns this', () => {
|
||||||
|
expect(output).to.eql(instance);
|
||||||
|
});
|
||||||
|
|
||||||
it('returns early', () => {
|
it('returns early', () => {
|
||||||
expect(containerOuterCloseSpy.called).to.equal(false);
|
expect(containerOuterCloseSpy.called).to.equal(false);
|
||||||
|
@ -735,7 +737,9 @@ describe('choices', () => {
|
||||||
output = instance.highlightItem();
|
output = instance.highlightItem();
|
||||||
});
|
});
|
||||||
|
|
||||||
returnsInstance(output);
|
it('returns this', () => {
|
||||||
|
expect(output).to.eql(instance);
|
||||||
|
});
|
||||||
|
|
||||||
it('returns early', () => {
|
it('returns early', () => {
|
||||||
expect(passedElementTriggerEventStub.called).to.equal(false);
|
expect(passedElementTriggerEventStub.called).to.equal(false);
|
||||||
|
@ -745,7 +749,7 @@ describe('choices', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('item passed', () => {
|
describe('item passed', () => {
|
||||||
const item = {
|
const item: Item = {
|
||||||
id: 1234,
|
id: 1234,
|
||||||
value: 'Test',
|
value: 'Test',
|
||||||
label: 'Test',
|
label: 'Test',
|
||||||
|
@ -756,7 +760,9 @@ describe('choices', () => {
|
||||||
output = instance.highlightItem(item, true);
|
output = instance.highlightItem(item, true);
|
||||||
});
|
});
|
||||||
|
|
||||||
returnsInstance(output);
|
it('returns this', () => {
|
||||||
|
expect(output).to.eql(instance);
|
||||||
|
});
|
||||||
|
|
||||||
it('dispatches highlightItem action with correct arguments', () => {
|
it('dispatches highlightItem action with correct arguments', () => {
|
||||||
expect(storeDispatchSpy.called).to.equal(true);
|
expect(storeDispatchSpy.called).to.equal(true);
|
||||||
|
@ -817,7 +823,9 @@ describe('choices', () => {
|
||||||
expect(passedElementTriggerEventStub.called).to.equal(false);
|
expect(passedElementTriggerEventStub.called).to.equal(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
returnsInstance(output);
|
it('returns this', () => {
|
||||||
|
expect(output).to.eql(instance);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -850,7 +858,9 @@ describe('choices', () => {
|
||||||
output = instance.unhighlightItem();
|
output = instance.unhighlightItem();
|
||||||
});
|
});
|
||||||
|
|
||||||
returnsInstance(output);
|
it('returns this', () => {
|
||||||
|
expect(output).to.eql(instance);
|
||||||
|
});
|
||||||
|
|
||||||
it('returns early', () => {
|
it('returns early', () => {
|
||||||
expect(passedElementTriggerEventStub.called).to.equal(false);
|
expect(passedElementTriggerEventStub.called).to.equal(false);
|
||||||
|
@ -860,7 +870,7 @@ describe('choices', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('item passed', () => {
|
describe('item passed', () => {
|
||||||
const item = {
|
const item: Item = {
|
||||||
id: 1234,
|
id: 1234,
|
||||||
value: 'Test',
|
value: 'Test',
|
||||||
label: 'Test',
|
label: 'Test',
|
||||||
|
@ -871,7 +881,9 @@ describe('choices', () => {
|
||||||
output = instance.unhighlightItem(item, true);
|
output = instance.unhighlightItem(item, true);
|
||||||
});
|
});
|
||||||
|
|
||||||
returnsInstance(output);
|
it('returns this', () => {
|
||||||
|
expect(output).to.eql(instance);
|
||||||
|
});
|
||||||
|
|
||||||
it('dispatches highlightItem action with correct arguments', () => {
|
it('dispatches highlightItem action with correct arguments', () => {
|
||||||
expect(storeDispatchSpy.called).to.equal(true);
|
expect(storeDispatchSpy.called).to.equal(true);
|
||||||
|
@ -932,7 +944,9 @@ describe('choices', () => {
|
||||||
expect(passedElementTriggerEventStub.called).to.equal(false);
|
expect(passedElementTriggerEventStub.called).to.equal(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
returnsInstance(output);
|
it('returns this', () => {
|
||||||
|
expect(output).to.eql(instance);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -966,7 +980,9 @@ describe('choices', () => {
|
||||||
storeGetItemsStub.reset();
|
storeGetItemsStub.reset();
|
||||||
});
|
});
|
||||||
|
|
||||||
returnsInstance(output);
|
it('returns this', () => {
|
||||||
|
expect(output).to.eql(instance);
|
||||||
|
});
|
||||||
|
|
||||||
it('highlights each item in store', () => {
|
it('highlights each item in store', () => {
|
||||||
expect(highlightItemStub.callCount).to.equal(items.length);
|
expect(highlightItemStub.callCount).to.equal(items.length);
|
||||||
|
@ -1004,7 +1020,9 @@ describe('choices', () => {
|
||||||
storeGetItemsStub.reset();
|
storeGetItemsStub.reset();
|
||||||
});
|
});
|
||||||
|
|
||||||
returnsInstance(output);
|
it('returns this', () => {
|
||||||
|
expect(output).to.eql(instance);
|
||||||
|
});
|
||||||
|
|
||||||
it('unhighlights each item in store', () => {
|
it('unhighlights each item in store', () => {
|
||||||
expect(unhighlightItemStub.callCount).to.equal(items.length);
|
expect(unhighlightItemStub.callCount).to.equal(items.length);
|
||||||
|
@ -1027,7 +1045,9 @@ describe('choices', () => {
|
||||||
instance._store.dispatch.reset();
|
instance._store.dispatch.reset();
|
||||||
});
|
});
|
||||||
|
|
||||||
returnsInstance(output);
|
it('returns this', () => {
|
||||||
|
expect(output).to.eql(instance);
|
||||||
|
});
|
||||||
|
|
||||||
it('dispatches clearChoices action', () => {
|
it('dispatches clearChoices action', () => {
|
||||||
expect(storeDispatchStub.lastCall.args[0]).to.eql({
|
expect(storeDispatchStub.lastCall.args[0]).to.eql({
|
||||||
|
@ -1050,7 +1070,9 @@ describe('choices', () => {
|
||||||
instance._store.dispatch.reset();
|
instance._store.dispatch.reset();
|
||||||
});
|
});
|
||||||
|
|
||||||
returnsInstance(output);
|
it('returns this', () => {
|
||||||
|
expect(output).to.eql(instance);
|
||||||
|
});
|
||||||
|
|
||||||
it('dispatches clearAll action', () => {
|
it('dispatches clearAll action', () => {
|
||||||
expect(storeDispatchStub.lastCall.args[0]).to.eql({
|
expect(storeDispatchStub.lastCall.args[0]).to.eql({
|
||||||
|
@ -1075,7 +1097,9 @@ describe('choices', () => {
|
||||||
instance._store.dispatch.reset();
|
instance._store.dispatch.reset();
|
||||||
});
|
});
|
||||||
|
|
||||||
returnsInstance(output);
|
it('returns this', () => {
|
||||||
|
expect(output).to.eql(instance);
|
||||||
|
});
|
||||||
|
|
||||||
describe('text element', () => {
|
describe('text element', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
@ -1164,14 +1188,14 @@ describe('choices', () => {
|
||||||
const handleLoadingStateSpy = spy(choice, '_handleLoadingState');
|
const handleLoadingStateSpy = spy(choice, '_handleLoadingState');
|
||||||
|
|
||||||
let fetcherCalled = false;
|
let fetcherCalled = false;
|
||||||
const fetcher = async inst => {
|
const fetcher = async (inst): Promise<Choice[]> => {
|
||||||
expect(inst).to.eq(choice);
|
expect(inst).to.eq(choice);
|
||||||
fetcherCalled = true;
|
fetcherCalled = true;
|
||||||
await new Promise(resolve => setTimeout(resolve, 800));
|
await new Promise(resolve => setTimeout(resolve, 800));
|
||||||
|
|
||||||
return [
|
return [
|
||||||
{ label: 'l1', value: 'v1', customProperties: 'prop1' },
|
{ label: 'l1', value: 'v1', customProperties: { prop1: true } },
|
||||||
{ label: 'l2', value: 'v2', customProperties: 'prop2' },
|
{ label: 'l2', value: 'v2', customProperties: { prop2: false } },
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
expect(choice._store.choices.length).to.equal(0);
|
expect(choice._store.choices.length).to.equal(0);
|
||||||
|
@ -1211,7 +1235,9 @@ describe('choices', () => {
|
||||||
output = instance.setValue(values);
|
output = instance.setValue(values);
|
||||||
});
|
});
|
||||||
|
|
||||||
returnsInstance(output);
|
it('returns this', () => {
|
||||||
|
expect(output).to.eql(instance);
|
||||||
|
});
|
||||||
|
|
||||||
it('returns early', () => {
|
it('returns early', () => {
|
||||||
expect(setChoiceOrItemStub.called).to.equal(false);
|
expect(setChoiceOrItemStub.called).to.equal(false);
|
||||||
|
@ -1224,7 +1250,9 @@ describe('choices', () => {
|
||||||
output = instance.setValue(values);
|
output = instance.setValue(values);
|
||||||
});
|
});
|
||||||
|
|
||||||
returnsInstance(output);
|
it('returns this', () => {
|
||||||
|
expect(output).to.eql(instance);
|
||||||
|
});
|
||||||
|
|
||||||
it('sets each value', () => {
|
it('sets each value', () => {
|
||||||
expect(setChoiceOrItemStub.callCount).to.equal(2);
|
expect(setChoiceOrItemStub.callCount).to.equal(2);
|
||||||
|
@ -1252,7 +1280,9 @@ describe('choices', () => {
|
||||||
output = instance.setChoiceByValue([]);
|
output = instance.setChoiceByValue([]);
|
||||||
});
|
});
|
||||||
|
|
||||||
returnsInstance(output);
|
it('returns this', () => {
|
||||||
|
expect(output).to.eql(instance);
|
||||||
|
});
|
||||||
|
|
||||||
it('returns early', () => {
|
it('returns early', () => {
|
||||||
expect(findAndSelectChoiceByValueStub.called).to.equal(false);
|
expect(findAndSelectChoiceByValueStub.called).to.equal(false);
|
||||||
|
@ -1272,7 +1302,9 @@ describe('choices', () => {
|
||||||
output = instance.setChoiceByValue(value);
|
output = instance.setChoiceByValue(value);
|
||||||
});
|
});
|
||||||
|
|
||||||
returnsInstance(output);
|
it('returns this', () => {
|
||||||
|
expect(output).to.eql(instance);
|
||||||
|
});
|
||||||
|
|
||||||
it('sets each choice with same value', () => {
|
it('sets each choice with same value', () => {
|
||||||
expect(findAndSelectChoiceByValueStub.called).to.equal(true);
|
expect(findAndSelectChoiceByValueStub.called).to.equal(true);
|
||||||
|
@ -1289,7 +1321,9 @@ describe('choices', () => {
|
||||||
output = instance.setChoiceByValue(values);
|
output = instance.setChoiceByValue(values);
|
||||||
});
|
});
|
||||||
|
|
||||||
returnsInstance(output);
|
it('returns this', () => {
|
||||||
|
expect(output).to.eql(instance);
|
||||||
|
});
|
||||||
|
|
||||||
it('sets each choice with same value', () => {
|
it('sets each choice with same value', () => {
|
||||||
expect(findAndSelectChoiceByValueStub.callCount).to.equal(2);
|
expect(findAndSelectChoiceByValueStub.callCount).to.equal(2);
|
||||||
|
@ -1509,7 +1543,9 @@ describe('choices', () => {
|
||||||
output = instance.removeHighlightedItems();
|
output = instance.removeHighlightedItems();
|
||||||
});
|
});
|
||||||
|
|
||||||
returnsInstance(output);
|
it('returns this', () => {
|
||||||
|
expect(output).to.eql(instance);
|
||||||
|
});
|
||||||
|
|
||||||
it('removes each highlighted item in store', () => {
|
it('removes each highlighted item in store', () => {
|
||||||
expect(removeItemStub.callCount).to.equal(2);
|
expect(removeItemStub.callCount).to.equal(2);
|
||||||
|
@ -1521,7 +1557,9 @@ describe('choices', () => {
|
||||||
output = instance.removeHighlightedItems(true);
|
output = instance.removeHighlightedItems(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
returnsInstance(output);
|
it('returns this', () => {
|
||||||
|
expect(output).to.eql(instance);
|
||||||
|
});
|
||||||
|
|
||||||
it('triggers event with item value', () => {
|
it('triggers event with item value', () => {
|
||||||
expect(triggerChangeStub.callCount).to.equal(2);
|
expect(triggerChangeStub.callCount).to.equal(2);
|
||||||
|
@ -1538,7 +1576,7 @@ describe('choices', () => {
|
||||||
let containerOuterRemoveLoadingStateStub;
|
let containerOuterRemoveLoadingStateStub;
|
||||||
const value = 'value';
|
const value = 'value';
|
||||||
const label = 'label';
|
const label = 'label';
|
||||||
const choices = [
|
const choices: Choice[] = [
|
||||||
{
|
{
|
||||||
id: 1,
|
id: 1,
|
||||||
value: '1',
|
value: '1',
|
||||||
|
@ -1550,7 +1588,7 @@ describe('choices', () => {
|
||||||
label: 'Test 2',
|
label: 'Test 2',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
const groups = [
|
const groups: Group[] = [
|
||||||
{
|
{
|
||||||
...choices[0],
|
...choices[0],
|
||||||
choices,
|
choices,
|
||||||
|
@ -1679,7 +1717,7 @@ describe('choices', () => {
|
||||||
describe('private methods', () => {
|
describe('private methods', () => {
|
||||||
describe('_createGroupsFragment', () => {
|
describe('_createGroupsFragment', () => {
|
||||||
let _createChoicesFragmentStub;
|
let _createChoicesFragmentStub;
|
||||||
const choices = [
|
const choices: Choice[] = [
|
||||||
{
|
{
|
||||||
id: 1,
|
id: 1,
|
||||||
selected: true,
|
selected: true,
|
||||||
|
@ -1703,7 +1741,7 @@ describe('choices', () => {
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const groups = [
|
const groups: Group[] = [
|
||||||
{
|
{
|
||||||
id: 2,
|
id: 2,
|
||||||
value: 'Group 2',
|
value: 'Group 2',
|
||||||
|
|
|
@ -19,12 +19,13 @@ import {
|
||||||
SELECT_ONE_TYPE,
|
SELECT_ONE_TYPE,
|
||||||
SELECT_MULTIPLE_TYPE,
|
SELECT_MULTIPLE_TYPE,
|
||||||
} from './constants';
|
} from './constants';
|
||||||
import { TEMPLATES } from './templates';
|
import templates from './templates';
|
||||||
import {
|
import {
|
||||||
addChoice,
|
addChoice,
|
||||||
filterChoices,
|
filterChoices,
|
||||||
activateChoices,
|
activateChoices,
|
||||||
clearChoices,
|
clearChoices,
|
||||||
|
Result,
|
||||||
} from './actions/choices';
|
} from './actions/choices';
|
||||||
import { addItem, removeItem, highlightItem } from './actions/items';
|
import { addItem, removeItem, highlightItem } from './actions/items';
|
||||||
import { addGroup } from './actions/groups';
|
import { addGroup } from './actions/groups';
|
||||||
|
@ -41,7 +42,6 @@ import {
|
||||||
diff,
|
diff,
|
||||||
} from './lib/utils';
|
} from './lib/utils';
|
||||||
import {
|
import {
|
||||||
Templates,
|
|
||||||
Options,
|
Options,
|
||||||
Choice,
|
Choice,
|
||||||
Item,
|
Item,
|
||||||
|
@ -67,14 +67,14 @@ const USER_DEFAULTS: Partial<Options> = {};
|
||||||
class Choices {
|
class Choices {
|
||||||
static get defaults(): {
|
static get defaults(): {
|
||||||
options: Partial<Options>;
|
options: Partial<Options>;
|
||||||
templates: Templates;
|
templates: typeof templates;
|
||||||
} {
|
} {
|
||||||
return Object.preventExtensions({
|
return Object.preventExtensions({
|
||||||
get options(): Partial<Options> {
|
get options(): Partial<Options> {
|
||||||
return USER_DEFAULTS;
|
return USER_DEFAULTS;
|
||||||
},
|
},
|
||||||
get templates(): Templates {
|
get templates(): typeof templates {
|
||||||
return TEMPLATES;
|
return templates;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -94,7 +94,7 @@ class Choices {
|
||||||
_isSelectMultipleElement: boolean;
|
_isSelectMultipleElement: boolean;
|
||||||
_isSelectElement: boolean;
|
_isSelectElement: boolean;
|
||||||
_store: Store;
|
_store: Store;
|
||||||
_templates: Templates;
|
_templates: typeof templates;
|
||||||
_initialState: State;
|
_initialState: State;
|
||||||
_currentState: State;
|
_currentState: State;
|
||||||
_prevState: State;
|
_prevState: State;
|
||||||
|
@ -116,7 +116,11 @@ class Choices {
|
||||||
_presetItems: Item[] | string[];
|
_presetItems: Item[] | string[];
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
element: string | HTMLInputElement | HTMLSelectElement = '[data-choice]',
|
element:
|
||||||
|
| string
|
||||||
|
| Element
|
||||||
|
| HTMLInputElement
|
||||||
|
| HTMLSelectElement = '[data-choice]',
|
||||||
userConfig: Partial<Options> = {},
|
userConfig: Partial<Options> = {},
|
||||||
) {
|
) {
|
||||||
this.config = merge.all<Options>(
|
this.config = merge.all<Options>(
|
||||||
|
@ -183,7 +187,7 @@ class Choices {
|
||||||
this.passedElement = new WrappedSelect({
|
this.passedElement = new WrappedSelect({
|
||||||
element: passedElement as HTMLSelectElement,
|
element: passedElement as HTMLSelectElement,
|
||||||
classNames: this.config.classNames,
|
classNames: this.config.classNames,
|
||||||
template: (data: object): HTMLOptionElement =>
|
template: (data: Item): HTMLOptionElement =>
|
||||||
this._templates.option(data),
|
this._templates.option(data),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -337,7 +341,7 @@ class Choices {
|
||||||
(this.passedElement as WrappedSelect).options = this._presetOptions;
|
(this.passedElement as WrappedSelect).options = this._presetOptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
this._templates = TEMPLATES;
|
this._templates = templates;
|
||||||
this.initialised = false;
|
this.initialised = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -627,7 +631,7 @@ class Choices {
|
||||||
if (typeof Promise === 'function' && fetcher instanceof Promise) {
|
if (typeof Promise === 'function' && fetcher instanceof Promise) {
|
||||||
// that's a promise
|
// that's a promise
|
||||||
// eslint-disable-next-line compat/compat
|
// eslint-disable-next-line compat/compat
|
||||||
return new Promise(resolve => requestAnimationFrame(resolve))
|
return new Promise(resolve => requestAnimationFrame(resolve)) // eslint-disable-line compat/compat
|
||||||
.then(() => this._handleLoadingState(true))
|
.then(() => this._handleLoadingState(true))
|
||||||
.then(() => fetcher)
|
.then(() => fetcher)
|
||||||
.then((data: Choice[]) =>
|
.then((data: Choice[]) =>
|
||||||
|
@ -1294,9 +1298,12 @@ class Choices {
|
||||||
const haystack = this._store.searchableChoices;
|
const haystack = this._store.searchableChoices;
|
||||||
const needle = newValue;
|
const needle = newValue;
|
||||||
const keys = [...this.config.searchFields];
|
const keys = [...this.config.searchFields];
|
||||||
const options = Object.assign(this.config.fuseOptions, { keys });
|
const options = Object.assign(this.config.fuseOptions, {
|
||||||
|
keys,
|
||||||
|
includeMatches: true,
|
||||||
|
});
|
||||||
const fuse = new Fuse(haystack, options);
|
const fuse = new Fuse(haystack, options);
|
||||||
const results = fuse.search(needle);
|
const results: Result<Choice>[] = fuse.search(needle) as any[]; // see https://github.com/krisk/Fuse/issues/303
|
||||||
|
|
||||||
this._currentValue = newValue;
|
this._currentValue = newValue;
|
||||||
this._highlightPosition = 0;
|
this._highlightPosition = 0;
|
||||||
|
@ -2091,7 +2098,7 @@ class Choices {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_getTemplate<K extends keyof Templates>(template: K, ...args: any): any {
|
_getTemplate(template: string, ...args: any): any {
|
||||||
const { classNames } = this.config;
|
const { classNames } = this.config;
|
||||||
|
|
||||||
return this._templates[template].call(this, classNames, ...args);
|
return this._templates[template].call(this, classNames, ...args);
|
||||||
|
@ -2108,7 +2115,7 @@ class Choices {
|
||||||
userTemplates = callbackOnCreateTemplates.call(this, strToEl);
|
userTemplates = callbackOnCreateTemplates.call(this, strToEl);
|
||||||
}
|
}
|
||||||
|
|
||||||
this._templates = merge(TEMPLATES, userTemplates);
|
this._templates = merge(templates, userTemplates);
|
||||||
}
|
}
|
||||||
|
|
||||||
_createElements(): void {
|
_createElements(): void {
|
||||||
|
|
|
@ -13,7 +13,7 @@ describe('components/container', () => {
|
||||||
|
|
||||||
document.body.appendChild(element);
|
document.body.appendChild(element);
|
||||||
instance = new Container({
|
instance = new Container({
|
||||||
element: document.getElementById('container'),
|
element: document.getElementById('container') as HTMLElement,
|
||||||
classNames: DEFAULT_CLASSNAMES,
|
classNames: DEFAULT_CLASSNAMES,
|
||||||
position: 'auto',
|
position: 'auto',
|
||||||
type: 'text',
|
type: 'text',
|
||||||
|
@ -383,7 +383,7 @@ describe('components/container', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
document.getElementById('wrap-test').remove();
|
document.getElementById('wrap-test')!.remove();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('wraps passed element inside element', () => {
|
it('wraps passed element inside element', () => {
|
||||||
|
@ -406,7 +406,7 @@ describe('components/container', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
document.body.removeChild(document.getElementById('unwrap-test'));
|
document.body.removeChild(document.getElementById('unwrap-test') as Node);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('moves wrapped element outside of element', () => {
|
it('moves wrapped element outside of element', () => {
|
||||||
|
|
|
@ -13,7 +13,6 @@ describe('components/input', () => {
|
||||||
element: choicesElement,
|
element: choicesElement,
|
||||||
type: 'text',
|
type: 'text',
|
||||||
classNames: DEFAULT_CLASSNAMES,
|
classNames: DEFAULT_CLASSNAMES,
|
||||||
placeholderValue: null,
|
|
||||||
preventPaste: false,
|
preventPaste: false,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -49,7 +48,7 @@ describe('components/input', () => {
|
||||||
expect(['input', 'paste', 'focus', 'blur']).to.have.members(
|
expect(['input', 'paste', 'focus', 'blur']).to.have.members(
|
||||||
Array.from(
|
Array.from(
|
||||||
{ length: addEventListenerStub.callCount },
|
{ length: addEventListenerStub.callCount },
|
||||||
(v, i) => addEventListenerStub.getCall(i).args[0],
|
(_, i) => addEventListenerStub.getCall(i).args[0],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
|
@ -31,7 +31,7 @@ export default class List {
|
||||||
this.element.scrollTop = 0;
|
this.element.scrollTop = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
scrollToChildElement(element: Element, direction: 1 | -1): void {
|
scrollToChildElement(element: HTMLElement, direction: 1 | -1): void {
|
||||||
if (!element) {
|
if (!element) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,10 +34,12 @@ describe('components/wrappedInput', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('inherited methods', () => {
|
describe('inherited methods', () => {
|
||||||
['conceal', 'reveal', 'enable', 'disable'].forEach(method => {
|
const methods: string[] = ['conceal', 'reveal', 'enable', 'disable'];
|
||||||
|
|
||||||
|
methods.forEach(method => {
|
||||||
describe(method, () => {
|
describe(method, () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
stub(WrappedElement.prototype, method);
|
stub(WrappedElement.prototype, method as keyof WrappedElement);
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import WrappedElement from './wrapped-element';
|
import WrappedElement from './wrapped-element';
|
||||||
import { ClassNames, Item } from '../interfaces';
|
import { ClassNames } from '../interfaces';
|
||||||
|
|
||||||
export default class WrappedInput extends WrappedElement {
|
export default class WrappedInput extends WrappedElement {
|
||||||
element: HTMLInputElement;
|
element: HTMLInputElement;
|
||||||
|
|
|
@ -32,7 +32,7 @@ describe('components/wrappedSelect', () => {
|
||||||
document.body.appendChild(element);
|
document.body.appendChild(element);
|
||||||
|
|
||||||
instance = new WrappedSelect({
|
instance = new WrappedSelect({
|
||||||
element: document.getElementById('target'),
|
element: document.getElementById('target') as HTMLSelectElement,
|
||||||
classNames: DEFAULT_CLASSNAMES,
|
classNames: DEFAULT_CLASSNAMES,
|
||||||
template: spy(Templates.option),
|
template: spy(Templates.option),
|
||||||
});
|
});
|
||||||
|
@ -54,9 +54,11 @@ describe('components/wrappedSelect', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('inherited methods', () => {
|
describe('inherited methods', () => {
|
||||||
['conceal', 'reveal', 'enable', 'disable'].forEach(method => {
|
const methods: string[] = ['conceal', 'reveal', 'enable', 'disable'];
|
||||||
|
|
||||||
|
methods.forEach(method => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
stub(WrappedElement.prototype, method);
|
stub(WrappedElement.prototype, method as keyof WrappedElement);
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import WrappedElement from './wrapped-element';
|
import WrappedElement from './wrapped-element';
|
||||||
import { ClassNames, Item, Choice } from '../interfaces';
|
import { ClassNames, Item } from '../interfaces';
|
||||||
|
|
||||||
export default class WrappedSelect extends WrappedElement {
|
export default class WrappedSelect extends WrappedElement {
|
||||||
element: HTMLSelectElement;
|
element: HTMLSelectElement;
|
||||||
|
@ -35,7 +35,7 @@ export default class WrappedSelect extends WrappedElement {
|
||||||
return Array.from(this.element.options);
|
return Array.from(this.element.options);
|
||||||
}
|
}
|
||||||
|
|
||||||
set options(options: Item[] | Choice[]): void {
|
set options(options: Item[] | HTMLOptionElement[]) {
|
||||||
const fragment = document.createDocumentFragment();
|
const fragment = document.createDocumentFragment();
|
||||||
const addOptionToFragment = (data): void => {
|
const addOptionToFragment = (data): void => {
|
||||||
// Create a standard select option
|
// Create a standard select option
|
||||||
|
|
|
@ -21,7 +21,7 @@ export interface Choice {
|
||||||
customProperties?: Record<string, any>;
|
customProperties?: Record<string, any>;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
active?: boolean;
|
active?: boolean;
|
||||||
elementId?: string;
|
elementId?: number;
|
||||||
groupId?: number;
|
groupId?: number;
|
||||||
keyCode?: number;
|
keyCode?: number;
|
||||||
label: string;
|
label: string;
|
||||||
|
@ -183,64 +183,6 @@ export type ActionType =
|
||||||
| 'RESET_TO'
|
| 'RESET_TO'
|
||||||
| 'SET_IS_LOADING';
|
| 'SET_IS_LOADING';
|
||||||
|
|
||||||
export interface Templates {
|
|
||||||
containerOuter: (
|
|
||||||
this: Choices,
|
|
||||||
classNames: ClassNames,
|
|
||||||
direction: HTMLElement['dir'],
|
|
||||||
isSelectElement: boolean,
|
|
||||||
isSelectOneElement: boolean,
|
|
||||||
searchEnabled: boolean,
|
|
||||||
passedElementType: PassedElement['type'],
|
|
||||||
) => HTMLElement;
|
|
||||||
containerInner: (this: Choices, classNames: ClassNames) => HTMLElement;
|
|
||||||
itemList: (
|
|
||||||
this: Choices,
|
|
||||||
classNames: ClassNames,
|
|
||||||
isSelectOneElement: boolean,
|
|
||||||
) => HTMLElement;
|
|
||||||
placeholder: (
|
|
||||||
this: Choices,
|
|
||||||
classNames: ClassNames,
|
|
||||||
value: string,
|
|
||||||
) => HTMLElement;
|
|
||||||
item: (
|
|
||||||
this: Choices,
|
|
||||||
classNames: ClassNames,
|
|
||||||
data: Choice,
|
|
||||||
removeItemButton: boolean,
|
|
||||||
) => HTMLElement;
|
|
||||||
choiceList: (
|
|
||||||
this: Choices,
|
|
||||||
classNames: ClassNames,
|
|
||||||
isSelectOneElement: boolean,
|
|
||||||
) => HTMLElement;
|
|
||||||
choiceGroup: (
|
|
||||||
this: Choices,
|
|
||||||
classNames: ClassNames,
|
|
||||||
data: Choice,
|
|
||||||
) => HTMLElement;
|
|
||||||
choice: (
|
|
||||||
this: Choices,
|
|
||||||
classNames: ClassNames,
|
|
||||||
data: Choice,
|
|
||||||
selectText: string,
|
|
||||||
) => HTMLElement;
|
|
||||||
input: (
|
|
||||||
this: Choices,
|
|
||||||
classNames: ClassNames,
|
|
||||||
placeholderValue: string,
|
|
||||||
) => HTMLInputElement;
|
|
||||||
dropdown: (this: Choices, classNames: ClassNames) => HTMLElement;
|
|
||||||
notice: (
|
|
||||||
this: Choices,
|
|
||||||
classNames: ClassNames,
|
|
||||||
label: string,
|
|
||||||
type: '' | 'no-results' | 'no-choices',
|
|
||||||
) => HTMLElement;
|
|
||||||
option: (data: object) => HTMLOptionElement;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Classes added to HTML generated by By default classnames follow the BEM notation. */
|
/** Classes added to HTML generated by By default classnames follow the BEM notation. */
|
||||||
export interface ClassNames {
|
export interface ClassNames {
|
||||||
/** @default 'choices' */
|
/** @default 'choices' */
|
||||||
|
@ -795,9 +737,7 @@ export interface Options {
|
||||||
*
|
*
|
||||||
* @default null
|
* @default null
|
||||||
*/
|
*/
|
||||||
callbackOnCreateTemplates:
|
callbackOnCreateTemplates: ((template: Types.strToEl) => void) | null;
|
||||||
| ((template: Types.strToEl) => Partial<Templates>)
|
|
||||||
| null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface KeyDownAction {
|
export interface KeyDownAction {
|
||||||
|
@ -815,9 +755,9 @@ export interface Notice {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface State {
|
export interface State {
|
||||||
choices: object[];
|
choices: Choice[];
|
||||||
groups: object[];
|
groups: Group[];
|
||||||
items: object[];
|
items: Item[];
|
||||||
general: {
|
general: {
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
/* eslint-disable no-new-wrappers */
|
||||||
import { expect } from 'chai';
|
import { expect } from 'chai';
|
||||||
import { stub } from 'sinon';
|
import { stub } from 'sinon';
|
||||||
import {
|
import {
|
||||||
|
@ -140,19 +141,19 @@ describe('utils', () => {
|
||||||
|
|
||||||
it('sorts by label alphabetically', () => {
|
it('sorts by label alphabetically', () => {
|
||||||
const values = [
|
const values = [
|
||||||
{ label: 'The Strokes' },
|
{ value: '0', label: 'The Strokes' },
|
||||||
{ label: 'Arctic Monkeys' },
|
{ value: '0', label: 'Arctic Monkeys' },
|
||||||
{ label: 'Oasis' },
|
{ value: '0', label: 'Oasis' },
|
||||||
{ label: 'Tame Impala' },
|
{ value: '0', label: 'Tame Impala' },
|
||||||
];
|
];
|
||||||
|
|
||||||
const output = values.sort(sortByAlpha);
|
const output = values.sort(sortByAlpha);
|
||||||
|
|
||||||
expect(output).to.eql([
|
expect(output).to.eql([
|
||||||
{ label: 'Arctic Monkeys' },
|
{ value: '0', label: 'Arctic Monkeys' },
|
||||||
{ label: 'Oasis' },
|
{ value: '0', label: 'Oasis' },
|
||||||
{ label: 'Tame Impala' },
|
{ value: '0', label: 'Tame Impala' },
|
||||||
{ label: 'The Strokes' },
|
{ value: '0', label: 'The Strokes' },
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -185,12 +186,12 @@ describe('utils', () => {
|
||||||
const fakeElement = {
|
const fakeElement = {
|
||||||
dispatchEvent: stub(),
|
dispatchEvent: stub(),
|
||||||
};
|
};
|
||||||
const eventType = 'testEvent';
|
const eventType = 'addItem';
|
||||||
const customArgs = {
|
const customArgs = {
|
||||||
testing: true,
|
testing: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
dispatchEvent(fakeElement, eventType, customArgs);
|
dispatchEvent(fakeElement as any, eventType, customArgs);
|
||||||
|
|
||||||
expect(fakeElement.dispatchEvent.called).to.equal(true);
|
expect(fakeElement.dispatchEvent.called).to.equal(true);
|
||||||
const event = fakeElement.dispatchEvent.lastCall.args[0];
|
const event = fakeElement.dispatchEvent.lastCall.args[0];
|
||||||
|
|
|
@ -126,7 +126,10 @@ export const sortByAlpha = (
|
||||||
numeric: true,
|
numeric: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
export const sortByScore = (a: Choice, b: Choice): number => {
|
export const sortByScore = (
|
||||||
|
a: Pick<Choice, 'score'>,
|
||||||
|
b: Pick<Choice, 'score'>,
|
||||||
|
): number => {
|
||||||
const { score: scoreA = 0 } = a;
|
const { score: scoreA = 0 } = a;
|
||||||
const { score: scoreB = 0 } = b;
|
const { score: scoreB = 0 } = b;
|
||||||
|
|
||||||
|
|
|
@ -1,21 +1,22 @@
|
||||||
import { expect } from 'chai';
|
import { expect } from 'chai';
|
||||||
import choices, { defaultState } from './choices';
|
import choices, { defaultState } from './choices';
|
||||||
|
import { Choice } from '../interfaces';
|
||||||
|
|
||||||
describe('reducers/choices', () => {
|
describe('reducers/choices', () => {
|
||||||
it('should return same state when no action matches', () => {
|
it('should return same state when no action matches', () => {
|
||||||
expect(choices(defaultState, {})).to.equal(defaultState);
|
expect(choices(defaultState, {} as any)).to.equal(defaultState);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('when choices do not exist', () => {
|
describe('when choices do not exist', () => {
|
||||||
describe('ADD_CHOICE', () => {
|
describe('ADD_CHOICE', () => {
|
||||||
const value = 'test';
|
const value = 'test';
|
||||||
const label = 'test';
|
const label = 'test';
|
||||||
const id = 'test';
|
const id = 1;
|
||||||
const groupId = 'test';
|
const groupId = 1;
|
||||||
const disabled = false;
|
const disabled = false;
|
||||||
const elementId = 'test';
|
const elementId = 1;
|
||||||
const customProperties = 'test';
|
const customProperties = { test: true };
|
||||||
const placeholder = 'test';
|
const placeholder = true;
|
||||||
|
|
||||||
describe('passing expected values', () => {
|
describe('passing expected values', () => {
|
||||||
it('adds choice', () => {
|
it('adds choice', () => {
|
||||||
|
@ -75,7 +76,7 @@ describe('reducers/choices', () => {
|
||||||
const actualResponse = choices(undefined, {
|
const actualResponse = choices(undefined, {
|
||||||
type: 'ADD_CHOICE',
|
type: 'ADD_CHOICE',
|
||||||
value,
|
value,
|
||||||
label: null,
|
label: undefined,
|
||||||
id,
|
id,
|
||||||
groupId,
|
groupId,
|
||||||
disabled,
|
disabled,
|
||||||
|
@ -110,7 +111,7 @@ describe('reducers/choices', () => {
|
||||||
const actualResponse = choices(undefined, {
|
const actualResponse = choices(undefined, {
|
||||||
type: 'ADD_CHOICE',
|
type: 'ADD_CHOICE',
|
||||||
value,
|
value,
|
||||||
label: null,
|
label: undefined,
|
||||||
id,
|
id,
|
||||||
groupId,
|
groupId,
|
||||||
disabled,
|
disabled,
|
||||||
|
@ -178,9 +179,7 @@ describe('reducers/choices', () => {
|
||||||
type: 'FILTER_CHOICES',
|
type: 'FILTER_CHOICES',
|
||||||
results: [
|
results: [
|
||||||
{
|
{
|
||||||
item: {
|
item: { id } as Choice,
|
||||||
id,
|
|
||||||
},
|
|
||||||
score,
|
score,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@ -248,11 +247,10 @@ describe('reducers/choices', () => {
|
||||||
expect(actualResponse).to.eql(expectedResponse);
|
expect(actualResponse).to.eql(expectedResponse);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('activates all choices if activateOptions flag passed', () => {
|
it('activates all choices if active flag passed', () => {
|
||||||
const clonedState = state.slice(0);
|
const clonedState = state.slice(0);
|
||||||
const actualResponse = choices(clonedState, {
|
const actualResponse = choices(clonedState, {
|
||||||
type: 'ADD_ITEM',
|
type: 'ADD_ITEM',
|
||||||
activateOptions: true,
|
|
||||||
active: true,
|
active: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -265,7 +263,6 @@ describe('reducers/choices', () => {
|
||||||
const clonedState = state.slice(0);
|
const clonedState = state.slice(0);
|
||||||
const actualResponse = choices(clonedState, {
|
const actualResponse = choices(clonedState, {
|
||||||
type: 'ADD_ITEM',
|
type: 'ADD_ITEM',
|
||||||
activateOptions: false,
|
|
||||||
choiceId: undefined,
|
choiceId: undefined,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Choice, State } from '../interfaces';
|
import { Choice } from '../interfaces';
|
||||||
import {
|
import {
|
||||||
AddChoiceAction,
|
AddChoiceAction,
|
||||||
FilterChoicesAction,
|
FilterChoicesAction,
|
||||||
|
@ -9,11 +9,6 @@ import { AddItemAction, RemoveItemAction } from '../actions/items';
|
||||||
|
|
||||||
export const defaultState = [];
|
export const defaultState = [];
|
||||||
|
|
||||||
interface Result {
|
|
||||||
item: Choice;
|
|
||||||
score: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
type ActionTypes =
|
type ActionTypes =
|
||||||
| AddChoiceAction
|
| AddChoiceAction
|
||||||
| FilterChoicesAction
|
| FilterChoicesAction
|
||||||
|
@ -25,7 +20,7 @@ type ActionTypes =
|
||||||
export default function choices(
|
export default function choices(
|
||||||
state: Choice[] = defaultState,
|
state: Choice[] = defaultState,
|
||||||
action: ActionTypes,
|
action: ActionTypes,
|
||||||
): State['choices'] {
|
): Choice[] {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case 'ADD_CHOICE': {
|
case 'ADD_CHOICE': {
|
||||||
const addChoiceAction = action as AddChoiceAction;
|
const addChoiceAction = action as AddChoiceAction;
|
||||||
|
@ -41,7 +36,6 @@ export default function choices(
|
||||||
score: 9999,
|
score: 9999,
|
||||||
customProperties: addChoiceAction.customProperties,
|
customProperties: addChoiceAction.customProperties,
|
||||||
placeholder: addChoiceAction.placeholder || false,
|
placeholder: addChoiceAction.placeholder || false,
|
||||||
keyCode: null,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -49,7 +43,7 @@ export default function choices(
|
||||||
A selected choice has been added to the passed input's value (added as an item)
|
A selected choice has been added to the passed input's value (added as an item)
|
||||||
An active choice appears within the choice dropdown
|
An active choice appears within the choice dropdown
|
||||||
*/
|
*/
|
||||||
return [...state, choice];
|
return [...state, choice as Choice];
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'ADD_ITEM': {
|
case 'ADD_ITEM': {
|
||||||
|
@ -97,17 +91,15 @@ export default function choices(
|
||||||
const choice = obj;
|
const choice = obj;
|
||||||
// Set active state based on whether choice is
|
// Set active state based on whether choice is
|
||||||
// within filtered results
|
// within filtered results
|
||||||
choice.active = filterChoicesAction.results.some(
|
choice.active = filterChoicesAction.results.some(({ item, score }) => {
|
||||||
({ item, score }: Result) => {
|
if (item.id === choice.id) {
|
||||||
if (item.id === choice.id) {
|
choice.score = score;
|
||||||
choice.score = score;
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
},
|
});
|
||||||
);
|
|
||||||
|
|
||||||
return choice;
|
return choice;
|
||||||
});
|
});
|
||||||
|
|
|
@ -3,7 +3,7 @@ import general, { defaultState } from './general';
|
||||||
|
|
||||||
describe('reducers/general', () => {
|
describe('reducers/general', () => {
|
||||||
it('should return same state when no action matches', () => {
|
it('should return same state when no action matches', () => {
|
||||||
expect(general(defaultState, {})).to.equal(defaultState);
|
expect(general(defaultState, {} as any)).to.equal(defaultState);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('SET_IS_LOADING', () => {
|
describe('SET_IS_LOADING', () => {
|
||||||
|
|
|
@ -3,13 +3,13 @@ import groups, { defaultState } from './groups';
|
||||||
|
|
||||||
describe('reducers/groups', () => {
|
describe('reducers/groups', () => {
|
||||||
it('should return same state when no action matches', () => {
|
it('should return same state when no action matches', () => {
|
||||||
expect(groups(defaultState, {})).to.equal(defaultState);
|
expect(groups(defaultState, {} as any)).to.equal(defaultState);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('when groups do not exist', () => {
|
describe('when groups do not exist', () => {
|
||||||
describe('ADD_GROUP', () => {
|
describe('ADD_GROUP', () => {
|
||||||
it('adds group', () => {
|
it('adds group', () => {
|
||||||
const id = '1';
|
const id = 1;
|
||||||
const value = 'Group one';
|
const value = 'Group one';
|
||||||
const active = true;
|
const active = true;
|
||||||
const disabled = false;
|
const disabled = false;
|
||||||
|
|
|
@ -11,9 +11,9 @@ describe('reducers/rootReducer', () => {
|
||||||
it('returns expected reducers', () => {
|
it('returns expected reducers', () => {
|
||||||
const state = store.getState();
|
const state = store.getState();
|
||||||
|
|
||||||
expect(state.groups).to.equal(groups(undefined, {}));
|
expect(state.groups).to.equal(groups(undefined, {} as any));
|
||||||
expect(state.choices).to.equal(choices(undefined, {}));
|
expect(state.choices).to.equal(choices(undefined, {} as any));
|
||||||
expect(state.items).to.equal(items(undefined, {}));
|
expect(state.items).to.equal(items(undefined, {} as any));
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('CLEAR_ALL', () => {
|
describe('CLEAR_ALL', () => {
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
import { expect } from 'chai';
|
import { expect } from 'chai';
|
||||||
import items, { defaultState } from './items';
|
import items, { defaultState } from './items';
|
||||||
|
import { RemoveItemAction } from '../actions/items';
|
||||||
|
|
||||||
describe('reducers/items', () => {
|
describe('reducers/items', () => {
|
||||||
it('should return same state when no action matches', () => {
|
it('should return same state when no action matches', () => {
|
||||||
expect(items(defaultState, {})).to.equal(defaultState);
|
expect(items(defaultState, {} as any)).to.equal(defaultState);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('when items do not exist', () => {
|
describe('when items do not exist', () => {
|
||||||
|
@ -148,7 +149,7 @@ describe('reducers/items', () => {
|
||||||
const actualResponse = items(clonedState, {
|
const actualResponse = items(clonedState, {
|
||||||
type: 'REMOVE_ITEM',
|
type: 'REMOVE_ITEM',
|
||||||
id,
|
id,
|
||||||
});
|
} as RemoveItemAction);
|
||||||
|
|
||||||
expect(actualResponse).to.eql(expectedResponse);
|
expect(actualResponse).to.eql(expectedResponse);
|
||||||
});
|
});
|
||||||
|
|
|
@ -33,7 +33,7 @@ describe('reducers/store', () => {
|
||||||
|
|
||||||
describe('subscribe', () => {
|
describe('subscribe', () => {
|
||||||
it('wraps redux subscribe method', () => {
|
it('wraps redux subscribe method', () => {
|
||||||
const onChange = () => {};
|
const onChange = (): void => {};
|
||||||
expect(subscribeStub.callCount).to.equal(0);
|
expect(subscribeStub.callCount).to.equal(0);
|
||||||
instance.subscribe(onChange);
|
instance.subscribe(onChange);
|
||||||
expect(subscribeStub.callCount).to.equal(1);
|
expect(subscribeStub.callCount).to.equal(1);
|
||||||
|
|
|
@ -7,7 +7,7 @@ import { strToEl } from './lib/utils';
|
||||||
* @param {HTMLElement} element1
|
* @param {HTMLElement} element1
|
||||||
* @param {HTMLElement} element2
|
* @param {HTMLElement} element2
|
||||||
*/
|
*/
|
||||||
function expectEqualElements(element1, element2) {
|
function expectEqualElements(element1, element2): void {
|
||||||
expect(element1.tagName).to.equal(element2.tagName);
|
expect(element1.tagName).to.equal(element2.tagName);
|
||||||
expect(element1.attributes.length).to.equal(element2.attributes.length);
|
expect(element1.attributes.length).to.equal(element2.attributes.length);
|
||||||
expect(Object.keys(element1.dataset)).to.have.members(
|
expect(Object.keys(element1.dataset)).to.have.members(
|
||||||
|
@ -516,11 +516,10 @@ describe('templates', () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
it('returns expected html', () => {
|
it('returns expected html', () => {
|
||||||
const value = 'test';
|
|
||||||
const expectedOutput = strToEl(
|
const expectedOutput = strToEl(
|
||||||
`<div class="${classes.list} ${classes.listDropdown}" aria-expanded="false"></div>`,
|
`<div class="${classes.list} ${classes.listDropdown}" aria-expanded="false"></div>`,
|
||||||
);
|
);
|
||||||
const actualOutput = templates.dropdown(classes, value);
|
const actualOutput = templates.dropdown(classes);
|
||||||
|
|
||||||
expectEqualElements(actualOutput, expectedOutput);
|
expectEqualElements(actualOutput, expectedOutput);
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,26 +1,19 @@
|
||||||
import {
|
import { ClassNames, Item, Choice, Group, PassedElement } from './interfaces';
|
||||||
Templates,
|
|
||||||
ClassNames,
|
|
||||||
Item,
|
|
||||||
Choice,
|
|
||||||
Group,
|
|
||||||
PassedElement,
|
|
||||||
} from './interfaces';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helpers to create HTML elements used by Choices
|
* Helpers to create HTML elements used by Choices
|
||||||
* Can be overridden by providing `callbackOnCreateTemplates` option
|
* Can be overridden by providing `callbackOnCreateTemplates` option
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export const TEMPLATES: Templates = {
|
const templates = {
|
||||||
containerOuter(
|
containerOuter(
|
||||||
{ containerOuter }: ClassNames,
|
{ containerOuter }: Pick<ClassNames, 'containerOuter'>,
|
||||||
dir: HTMLElement['dir'],
|
dir: HTMLElement['dir'],
|
||||||
isSelectElement: boolean,
|
isSelectElement: boolean,
|
||||||
isSelectOneElement: boolean,
|
isSelectOneElement: boolean,
|
||||||
searchEnabled: boolean,
|
searchEnabled: boolean,
|
||||||
passedElementType: PassedElement['type'],
|
passedElementType: PassedElement['type'],
|
||||||
) {
|
): HTMLDivElement {
|
||||||
const div = Object.assign(document.createElement('div'), {
|
const div = Object.assign(document.createElement('div'), {
|
||||||
className: containerOuter,
|
className: containerOuter,
|
||||||
});
|
});
|
||||||
|
@ -48,22 +41,31 @@ export const TEMPLATES: Templates = {
|
||||||
return div;
|
return div;
|
||||||
},
|
},
|
||||||
|
|
||||||
containerInner({ containerInner }: ClassNames) {
|
containerInner({
|
||||||
|
containerInner,
|
||||||
|
}: Pick<ClassNames, 'containerInner'>): HTMLDivElement {
|
||||||
return Object.assign(document.createElement('div'), {
|
return Object.assign(document.createElement('div'), {
|
||||||
className: containerInner,
|
className: containerInner,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
itemList(
|
itemList(
|
||||||
{ list, listSingle, listItems }: ClassNames,
|
{
|
||||||
|
list,
|
||||||
|
listSingle,
|
||||||
|
listItems,
|
||||||
|
}: Pick<ClassNames, 'list' | 'listSingle' | 'listItems'>,
|
||||||
isSelectOneElement: boolean,
|
isSelectOneElement: boolean,
|
||||||
) {
|
): HTMLDivElement {
|
||||||
return Object.assign(document.createElement('div'), {
|
return Object.assign(document.createElement('div'), {
|
||||||
className: `${list} ${isSelectOneElement ? listSingle : listItems}`,
|
className: `${list} ${isSelectOneElement ? listSingle : listItems}`,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
placeholder({ placeholder }: ClassNames, value: string) {
|
placeholder(
|
||||||
|
{ placeholder }: Pick<ClassNames, 'placeholder'>,
|
||||||
|
value: string,
|
||||||
|
): HTMLDivElement {
|
||||||
return Object.assign(document.createElement('div'), {
|
return Object.assign(document.createElement('div'), {
|
||||||
className: placeholder,
|
className: placeholder,
|
||||||
innerHTML: value,
|
innerHTML: value,
|
||||||
|
@ -71,7 +73,16 @@ export const TEMPLATES: Templates = {
|
||||||
},
|
},
|
||||||
|
|
||||||
item(
|
item(
|
||||||
{ item, button, highlightedState, itemSelectable, placeholder }: ClassNames,
|
{
|
||||||
|
item,
|
||||||
|
button,
|
||||||
|
highlightedState,
|
||||||
|
itemSelectable,
|
||||||
|
placeholder,
|
||||||
|
}: Pick<
|
||||||
|
ClassNames,
|
||||||
|
'item' | 'button' | 'highlightedState' | 'itemSelectable' | 'placeholder'
|
||||||
|
>,
|
||||||
{
|
{
|
||||||
id,
|
id,
|
||||||
value,
|
value,
|
||||||
|
@ -83,7 +94,7 @@ export const TEMPLATES: Templates = {
|
||||||
placeholder: isPlaceholder,
|
placeholder: isPlaceholder,
|
||||||
}: Item,
|
}: Item,
|
||||||
removeItemButton: boolean,
|
removeItemButton: boolean,
|
||||||
) {
|
): HTMLDivElement {
|
||||||
const div = Object.assign(document.createElement('div'), {
|
const div = Object.assign(document.createElement('div'), {
|
||||||
className: item,
|
className: item,
|
||||||
innerHTML: label,
|
innerHTML: label,
|
||||||
|
@ -133,7 +144,10 @@ export const TEMPLATES: Templates = {
|
||||||
return div;
|
return div;
|
||||||
},
|
},
|
||||||
|
|
||||||
choiceList({ list }: ClassNames, isSelectOneElement: boolean) {
|
choiceList(
|
||||||
|
{ list }: Pick<ClassNames, 'list'>,
|
||||||
|
isSelectOneElement: boolean,
|
||||||
|
): HTMLDivElement {
|
||||||
const div = Object.assign(document.createElement('div'), {
|
const div = Object.assign(document.createElement('div'), {
|
||||||
className: list,
|
className: list,
|
||||||
});
|
});
|
||||||
|
@ -147,9 +161,13 @@ export const TEMPLATES: Templates = {
|
||||||
},
|
},
|
||||||
|
|
||||||
choiceGroup(
|
choiceGroup(
|
||||||
{ group, groupHeading, itemDisabled }: ClassNames,
|
{
|
||||||
|
group,
|
||||||
|
groupHeading,
|
||||||
|
itemDisabled,
|
||||||
|
}: Pick<ClassNames, 'group' | 'groupHeading' | 'itemDisabled'>,
|
||||||
{ id, value, disabled }: Group,
|
{ id, value, disabled }: Group,
|
||||||
) {
|
): HTMLDivElement {
|
||||||
const div = Object.assign(document.createElement('div'), {
|
const div = Object.assign(document.createElement('div'), {
|
||||||
className: `${group} ${disabled ? itemDisabled : ''}`,
|
className: `${group} ${disabled ? itemDisabled : ''}`,
|
||||||
});
|
});
|
||||||
|
@ -184,7 +202,15 @@ export const TEMPLATES: Templates = {
|
||||||
selectedState,
|
selectedState,
|
||||||
itemDisabled,
|
itemDisabled,
|
||||||
placeholder,
|
placeholder,
|
||||||
}: ClassNames,
|
}: Pick<
|
||||||
|
ClassNames,
|
||||||
|
| 'item'
|
||||||
|
| 'itemChoice'
|
||||||
|
| 'itemSelectable'
|
||||||
|
| 'selectedState'
|
||||||
|
| 'itemDisabled'
|
||||||
|
| 'placeholder'
|
||||||
|
>,
|
||||||
{
|
{
|
||||||
id,
|
id,
|
||||||
value,
|
value,
|
||||||
|
@ -233,7 +259,7 @@ export const TEMPLATES: Templates = {
|
||||||
},
|
},
|
||||||
|
|
||||||
input(
|
input(
|
||||||
{ input, inputCloned }: ClassNames,
|
{ input, inputCloned }: Pick<ClassNames, 'input' | 'inputCloned'>,
|
||||||
placeholderValue: string,
|
placeholderValue: string,
|
||||||
): HTMLInputElement {
|
): HTMLInputElement {
|
||||||
const inp = Object.assign(document.createElement('input'), {
|
const inp = Object.assign(document.createElement('input'), {
|
||||||
|
@ -251,7 +277,10 @@ export const TEMPLATES: Templates = {
|
||||||
return inp;
|
return inp;
|
||||||
},
|
},
|
||||||
|
|
||||||
dropdown({ list, listDropdown }: ClassNames): HTMLDivElement {
|
dropdown({
|
||||||
|
list,
|
||||||
|
listDropdown,
|
||||||
|
}: Pick<ClassNames, 'list' | 'listDropdown'>): HTMLDivElement {
|
||||||
const div = document.createElement('div');
|
const div = document.createElement('div');
|
||||||
|
|
||||||
div.classList.add(list, listDropdown);
|
div.classList.add(list, listDropdown);
|
||||||
|
@ -261,7 +290,12 @@ export const TEMPLATES: Templates = {
|
||||||
},
|
},
|
||||||
|
|
||||||
notice(
|
notice(
|
||||||
{ item, itemChoice, noResults, noChoices }: ClassNames,
|
{
|
||||||
|
item,
|
||||||
|
itemChoice,
|
||||||
|
noResults,
|
||||||
|
noChoices,
|
||||||
|
}: Pick<ClassNames, 'item' | 'itemChoice' | 'noResults' | 'noChoices'>,
|
||||||
innerHTML: string,
|
innerHTML: string,
|
||||||
type: 'no-choices' | 'no-results' | '' = '',
|
type: 'no-choices' | 'no-results' | '' = '',
|
||||||
): HTMLDivElement {
|
): HTMLDivElement {
|
||||||
|
@ -298,4 +332,4 @@ export const TEMPLATES: Templates = {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export default TEMPLATES;
|
export default templates;
|
||||||
|
|
Loading…
Reference in a new issue