Destructure class args

This commit is contained in:
Josh Johnson 2018-05-21 17:01:03 +01:00
parent b3108835bb
commit d3a18e255b
15 changed files with 144 additions and 176 deletions

View file

@ -80,9 +80,16 @@ class Choices {
this.isValidElementType = this.isTextElement || this.isSelectElement;
if (this.isTextElement) {
this.passedElement = new WrappedInput(this, passedElement, this.config.classNames);
this.passedElement = new WrappedInput({
element: passedElement,
classNames: this.config.classNames,
delimiter: this.config.delimiter,
});
} else if (this.isSelectElement) {
this.passedElement = new WrappedSelect(this, passedElement, this.config.classNames);
this.passedElement = new WrappedSelect({
element: passedElement,
classNames: this.config.classNames,
});
}
if (!this.passedElement) {
@ -106,9 +113,9 @@ class Choices {
this.highlightPosition = 0;
this.canSearch = this.config.searchEnabled;
this.placeholder = false;
this.placeholderValue = false;
if (!this.isSelectOneElement) {
this.placeholder = this.config.placeholder ?
this.placeholderValue = this.config.placeholder ?
(this.config.placeholderValue || this.passedElement.element.getAttribute('placeholder')) :
false;
}
@ -1085,9 +1092,9 @@ class Choices {
this.containerOuter.removeLoadingState();
if (this.isSelectOneElement) {
placeholderItem.innerHTML = (this.placeholder || '');
placeholderItem.innerHTML = (this.placeholderValue || '');
} else {
this.input.placeholder = (this.placeholder || '');
this.input.placeholder = (this.placeholderValue || '');
}
}
}
@ -2166,12 +2173,39 @@ class Choices {
const input = this._getTemplate('input');
const dropdown = this._getTemplate('dropdown');
this.containerOuter = new Container(this, containerOuter, this.config.classNames);
this.containerInner = new Container(this, containerInner, this.config.classNames);
this.input = new Input(this, input, this.config.classNames);
this.choiceList = new List(this, choiceList, this.config.classNames);
this.itemList = new List(this, itemList, this.config.classNames);
this.dropdown = new Dropdown(this, dropdown, this.config.classNames);
this.containerOuter = new Container({
element: containerOuter,
classNames: this.config.classNames,
type: this.passedElement.element.type,
position: this.config.position,
});
this.containerInner = new Container({
element: containerInner,
classNames: this.config.classNames,
type: this.passedElement.element.type,
position: this.config.position,
});
this.input = new Input({
element: input,
classNames: this.config.classNames,
type: this.passedElement.element.type,
});
this.choiceList = new List({
element: choiceList,
});
this.itemList = new List({
element: itemList,
});
this.dropdown = new Dropdown({
element: dropdown,
classNames: this.config.classNames,
type: this.passedElement.element.type,
});
}
/**
@ -2189,8 +2223,8 @@ class Choices {
if (this.isSelectOneElement) {
this.input.placeholder = (this.config.searchPlaceholderValue || '');
} else if (this.placeholder) {
this.input.placeholder = this.placeholder;
} else if (this.placeholderValue) {
this.input.placeholder = this.placeholderValue;
this.input.setWidth(true);
}

View file

@ -1,16 +1,15 @@
import { getWindowHeight, wrap } from '../lib/utils';
export default class Container {
constructor(instance, element, classNames) {
this.parentInstance = instance;
this.element = element;
this.classNames = classNames;
this.config = instance.config;
constructor({ element, type, classNames, position }) {
Object.assign(this, { element, classNames, type, position });
this.isOpen = false;
this.isFlipped = false;
this.isFocussed = false;
this.isDisabled = false;
this.isLoading = false;
this.onFocus = this.onFocus.bind(this);
this.onBlur = this.onBlur.bind(this);
}
@ -57,16 +56,16 @@ export default class Container {
if (dropdownPos === undefined) {
return false;
}
// If flip is enabled and the dropdown bottom position is
// greater than the window height flip the dropdown.
let shouldFlip = false;
if (this.config.position === 'auto') {
if (this.position === 'auto') {
shouldFlip = dropdownPos >= windowHeight;
} else if (this.config.position === 'top') {
} else if (this.position === 'top') {
shouldFlip = true;
}
return shouldFlip;
}
@ -129,7 +128,7 @@ export default class Container {
enable() {
this.element.classList.remove(this.classNames.disabledState);
this.element.removeAttribute('aria-disabled');
if (this.parentInstance.isSelectOneElement) {
if (this.type === 'select-one') {
this.element.setAttribute('tabindex', '0');
}
this.isDisabled = false;
@ -141,7 +140,7 @@ export default class Container {
disable() {
this.element.classList.add(this.classNames.disabledState);
this.element.setAttribute('aria-disabled', 'true');
if (this.parentInstance.isSelectOneElement) {
if (this.type === 'select-one') {
this.element.setAttribute('tabindex', '-1');
}
this.isDisabled = true;

View file

@ -1,23 +1,23 @@
import { expect } from 'chai';
import { stub } from 'sinon';
import Container from './container';
import { DEFAULT_CLASSNAMES, DEFAULT_CONFIG } from '../constants';
import { DEFAULT_CLASSNAMES } from '../constants';
describe('components/container', () => {
let instance;
let choicesInstance;
let element;
beforeEach(() => {
choicesInstance = {
config: {
...DEFAULT_CONFIG,
},
};
element = document.createElement('div');
element.id = 'container';
document.body.appendChild(element);
instance = new Container(choicesInstance, document.getElementById('container'), DEFAULT_CLASSNAMES);
instance = new Container({
element: document.getElementById('container'),
classNames: DEFAULT_CLASSNAMES,
position: 'auto',
type: 'text',
});
});
afterEach(() => {
@ -27,10 +27,6 @@ describe('components/container', () => {
});
describe('constructor', () => {
it('assigns choices instance to class', () => {
expect(instance.parentInstance).to.eql(choicesInstance);
});
it('assigns choices element to class', () => {
expect(instance.element).to.eql(element);
});
@ -104,7 +100,7 @@ describe('components/container', () => {
describe('passing dropdownPos', () => {
describe('position config option set to "auto"', () => {
beforeEach(() => {
instance.config.position = 'auto';
instance.position = 'auto';
});
describe('dropdownPos is greater than window height', () => {
@ -122,7 +118,7 @@ describe('components/container', () => {
describe('position config option set to "top"', () => {
beforeEach(() => {
instance.config.position = 'top';
instance.position = 'top';
});
it('returns true', () => {
@ -132,7 +128,7 @@ describe('components/container', () => {
describe('position config option set to "bottom"', () => {
beforeEach(() => {
instance.config.position = 'bottom';
instance.position = 'bottom';
});
it('returns false', () => {
@ -310,7 +306,7 @@ describe('components/container', () => {
describe('select one element', () => {
beforeEach(() => {
instance.parentInstance.isSelectOneElement = true;
instance.type = 'select-one';
instance.enable();
});
@ -344,7 +340,7 @@ describe('components/container', () => {
describe('select one element', () => {
beforeEach(() => {
instance.parentInstance.isSelectOneElement = true;
instance.type = 'select-one';
instance.disable();
});

View file

@ -1,10 +1,7 @@
export default class Dropdown {
constructor(instance, element, classNames) {
this.parentInstance = instance;
this.element = element;
this.classNames = classNames;
this.dimensions = null;
this.position = null;
constructor({ element, type, classNames }) {
Object.assign(this, { element, type, classNames });
this.isActive = false;
}
@ -36,7 +33,7 @@ export default class Dropdown {
this.element.classList.add(this.classNames.activeState);
this.element.setAttribute('aria-expanded', 'true');
this.isActive = true;
return this.parentInstance;
return this;
}
/**
@ -48,6 +45,6 @@ export default class Dropdown {
this.element.classList.remove(this.classNames.activeState);
this.element.setAttribute('aria-expanded', 'false');
this.isActive = false;
return this.parentInstance;
return this;
}
}

View file

@ -1,23 +1,20 @@
import { expect } from 'chai';
import sinon from 'sinon';
import Dropdown from './dropdown';
import { DEFAULT_CLASSNAMES, DEFAULT_CONFIG } from '../constants';
import { DEFAULT_CLASSNAMES } from '../constants';
describe('components/dropdown', () => {
let instance;
let choicesInstance;
let choicesElement;
beforeEach(() => {
choicesInstance = {
config: {
...DEFAULT_CONFIG,
},
};
choicesElement = document.createElement('div');
document.body.appendChild(choicesElement);
instance = new Dropdown(choicesInstance, choicesElement, DEFAULT_CLASSNAMES);
instance = new Dropdown({
element: choicesElement,
type: 'text',
classNames: DEFAULT_CLASSNAMES,
});
});
afterEach(() => {
@ -26,10 +23,6 @@ describe('components/dropdown', () => {
});
describe('constructor', () => {
it('assigns choices instance to instance', () => {
expect(instance.parentInstance).to.eql(choicesInstance);
});
it('assigns choices element to instance', () => {
expect(instance.element).to.eql(choicesElement);
});
@ -125,8 +118,8 @@ describe('components/dropdown', () => {
expect(instance.isActive).to.equal(true);
});
it('returns parent instance', () => {
expect(actualResponse).to.eql(choicesInstance);
it('returns instance', () => {
expect(actualResponse).to.eql(instance);
});
});
@ -153,8 +146,8 @@ describe('components/dropdown', () => {
expect(instance.isActive).to.equal(false);
});
it('returns parent instance', () => {
expect(actualResponse).to.eql(choicesInstance);
it('returns instance', () => {
expect(actualResponse).to.eql(instance);
});
});
});

View file

@ -1,8 +1,9 @@
import { calcWidthOfInput } from '../lib/utils';
export default class Input {
constructor(instance, element, classNames) {
this.parentInstance = instance;
constructor({ element, type, classNames, placeholderValue }) {
Object.assign(this, { element, type, classNames, placeholderValue });
this.element = element;
this.classNames = classNames;
this.isFocussed = this.element === document.activeElement;
@ -47,7 +48,7 @@ export default class Input {
* @private
*/
onInput() {
if (!this.parentInstance.isSelectOneElement) {
if (this.type !== 'select-one') {
this.setWidth();
}
}
@ -60,21 +61,15 @@ export default class Input {
*/
onPaste(e) {
// Disable pasting into the input if option has been set
if (e.target === this.element && !this.parentInstance.config.paste) {
if (e.target === this.element && this.preventPaste) {
e.preventDefault();
}
}
/**
* Set focussed state
*/
onFocus() {
this.isFocussed = true;
}
/**
* Remove focussed state
*/
onBlur() {
this.isFocussed = false;
}
@ -115,7 +110,7 @@ export default class Input {
this.setWidth();
}
return this.parentInstance;
return this;
}
/**
@ -124,12 +119,12 @@ export default class Input {
* @return
*/
setWidth(enforceWidth) {
if (this.parentInstance.placeholder) {
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.parentInstance.placeholder.length / 1.25)) ||
this.element.value.length >= (this.placeholderValue.length / 1.25)) ||
enforceWidth
) {
this.element.style.width = this.calcWidth();

View file

@ -1,21 +1,21 @@
import { expect } from 'chai';
import { stub } from 'sinon';
import Input from './input';
import { DEFAULT_CLASSNAMES, DEFAULT_CONFIG } from '../constants';
import { DEFAULT_CLASSNAMES } from '../constants';
describe('components/input', () => {
let instance;
let choicesInstance;
let choicesElement;
beforeEach(() => {
choicesInstance = {
config: {
...DEFAULT_CONFIG,
},
};
choicesElement = document.createElement('input');
instance = new Input(choicesInstance, choicesElement, DEFAULT_CLASSNAMES);
instance = new Input({
element: choicesElement,
type: 'text',
classNames: DEFAULT_CLASSNAMES,
placeholderValue: null,
preventPaste: false,
});
});
afterEach(() => {
@ -24,10 +24,6 @@ describe('components/input', () => {
});
describe('constructor', () => {
it('assigns choices instance to class', () => {
expect(instance.parentInstance).to.eql(choicesInstance);
});
it('assigns choices element to class', () => {
expect(instance.element).to.eql(choicesElement);
});
@ -93,7 +89,7 @@ describe('components/input', () => {
describe('when element is select one', () => {
it('does not set input width', () => {
instance.parentInstance.isSelectOneElement = true;
instance.type = 'select-one';
instance.onInput();
expect(setWidthStub.callCount).to.equal(0);
});
@ -101,7 +97,7 @@ describe('components/input', () => {
describe('when element is not a select one', () => {
it('sets input width', () => {
instance.parentInstance.isSelectOneElement = false;
instance.type = 'text';
instance.onInput();
expect(setWidthStub.callCount).to.equal(1);
});
@ -120,7 +116,7 @@ describe('components/input', () => {
describe('when pasting is disabled and target is the element', () => {
it('prevents default pasting behaviour', () => {
instance.parentInstance.config.paste = false;
instance.preventPaste = true;
instance.onPaste(eventMock);
expect(eventMock.preventDefault.callCount).to.equal(1);
});
@ -128,7 +124,7 @@ describe('components/input', () => {
describe('when pasting is enabled', () => {
it('does not prevent default pasting behaviour', () => {
instance.parentInstance.config.paste = true;
instance.preventPaste = false;
instance.onPaste(eventMock);
expect(eventMock.preventDefault.callCount).to.equal(0);
});
@ -264,10 +260,9 @@ describe('components/input', () => {
expect(setWidthStub.callCount).to.equal(1);
});
it('returns parent instance', () => {
const actualResponse = instance.clear();
const expectedResponse = choicesInstance;
expect(actualResponse).to.eql(expectedResponse);
it('returns instance', () => {
const response = instance.clear();
expect(response).to.eql(instance);
});
});
@ -286,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.parentInstance.placeholder = '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();
@ -297,7 +292,7 @@ describe('components/input', () => {
describe('when width is enforced', () => {
it('sets the width of the element based on input value', () => {
instance.parentInstance.placeholder = '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);
@ -308,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.parentInstance.placeholder = 'This is a test';
instance.placeholderValue = 'This is a test';
instance.element.value = 'Test';
instance.setWidth();
expect(calcWidthStub.callCount).to.equal(0);

View file

@ -1,8 +1,7 @@
export default class List {
constructor(instance, element, classNames) {
this.parentInstance = instance;
this.element = element;
this.classNames = classNames;
constructor({ element }) {
Object.assign(this, { element });
this.scrollPos = this.element.scrollTop;
this.height = this.element.offsetHeight;
this.hasChildren = !!this.element.children;

View file

@ -1,20 +1,15 @@
import { expect } from 'chai';
import List from './list';
import { DEFAULT_CLASSNAMES, DEFAULT_CONFIG } from '../constants';
describe('components/list', () => {
let instance;
let choicesInstance;
let choicesElement;
beforeEach(() => {
choicesInstance = {
config: {
...DEFAULT_CONFIG,
},
};
choicesElement = document.createElement('div');
instance = new List(choicesInstance, choicesElement, DEFAULT_CLASSNAMES);
instance = new List({
element: choicesElement,
});
});
afterEach(() => {
@ -23,17 +18,9 @@ describe('components/list', () => {
});
describe('constructor', () => {
it('assigns choices instance to class', () => {
expect(instance.parentInstance).to.eql(choicesInstance);
});
it('assigns choices element to class', () => {
expect(instance.element).to.eql(choicesElement);
});
it('assigns classnames to class', () => {
expect(instance.classNames).to.eql(DEFAULT_CLASSNAMES);
});
});
describe('clear', () => {

View file

@ -1,10 +1,9 @@
import { dispatchEvent } from '../lib/utils';
export default class WrappedElement {
constructor(instance, element, classNames) {
this.parentInstance = instance;
this.element = element;
this.classNames = classNames;
constructor({ element, classNames }) {
Object.assign(this, { element, classNames });
this.isDisabled = false;
}

View file

@ -1,21 +1,17 @@
import { expect } from 'chai';
import WrappedElement from './wrapped-element';
import { DEFAULT_CLASSNAMES, DEFAULT_CONFIG } from '../constants';
import { DEFAULT_CLASSNAMES } from '../constants';
describe('components/wrappedElement', () => {
let instance;
let choicesInstance;
let element;
beforeEach(() => {
choicesInstance = {
config: {
...DEFAULT_CONFIG,
},
};
element = document.createElement('select');
instance = new WrappedElement(choicesInstance, element, DEFAULT_CLASSNAMES);
instance = new WrappedElement({
element,
classNames: DEFAULT_CLASSNAMES,
});
});
afterEach(() => {
@ -24,10 +20,6 @@ describe('components/wrappedElement', () => {
});
describe('constructor', () => {
it('assigns choices instance to class', () => {
expect(instance.parentInstance).to.eql(choicesInstance);
});
it('assigns choices element to class', () => {
expect(instance.element).to.eql(element);
});

View file

@ -2,16 +2,14 @@ import WrappedElement from './wrapped-element';
import { reduceToValues } from './../lib/utils';
export default class WrappedInput extends WrappedElement {
constructor(instance, element, classNames) {
super(instance, element, classNames);
this.parentInstance = instance;
this.element = element;
this.classNames = classNames;
constructor({ element, classNames, delimiter }) {
super({ element, classNames });
this.delimiter = delimiter;
}
set value(items) {
const itemsFiltered = reduceToValues(items);
const itemsFilteredString = itemsFiltered.join(this.parentInstance.config.delimiter);
const itemsFilteredString = itemsFiltered.join(this.delimiter);
this.element.setAttribute('value', itemsFilteredString);
this.element.value = itemsFilteredString;

View file

@ -2,21 +2,20 @@ import { expect } from 'chai';
import { stub } from 'sinon';
import WrappedElement from './wrapped-element';
import WrappedInput from './wrapped-input';
import { DEFAULT_CLASSNAMES, DEFAULT_CONFIG } from '../constants';
import { DEFAULT_CLASSNAMES } from '../constants';
describe('components/wrappedInput', () => {
let instance;
let choicesInstance;
let element;
const delimiter = '-';
beforeEach(() => {
choicesInstance = {
config: {
...DEFAULT_CONFIG,
},
};
element = document.createElement('input');
instance = new WrappedInput(choicesInstance, element, DEFAULT_CLASSNAMES);
instance = new WrappedInput({
element,
classNames: DEFAULT_CLASSNAMES,
delimiter,
});
});
afterEach(() => {
@ -25,10 +24,6 @@ describe('components/wrappedInput', () => {
});
describe('constructor', () => {
it('assigns choices instance to class', () => {
expect(instance.parentInstance).to.eql(choicesInstance);
});
it('assigns choices element to class', () => {
expect(instance.element).to.eql(element);
});
@ -77,7 +72,7 @@ describe('components/wrappedInput', () => {
it('sets delimited value of element based on passed data', () => {
expect(instance.element.value).to.equal('');
instance.value = data;
expect(instance.value).to.equal('Value 1,Value 2,Value 3');
expect(instance.value).to.equal(`Value 1${delimiter}Value 2${delimiter}Value 3`);
});
});
});

View file

@ -2,11 +2,8 @@ import WrappedElement from './wrapped-element';
import templates from './../templates';
export default class WrappedSelect extends WrappedElement {
constructor(instance, element, classNames) {
super(instance, element, classNames);
this.parentInstance = instance;
this.element = element;
this.classNames = classNames;
constructor({ element, classNames }) {
super({ element, classNames });
}
get placeholderOption() {

View file

@ -2,20 +2,13 @@ import { expect } from 'chai';
import { stub } from 'sinon';
import WrappedElement from './wrapped-element';
import WrappedSelect from './wrapped-select';
import { DEFAULT_CLASSNAMES, DEFAULT_CONFIG } from '../constants';
import { DEFAULT_CLASSNAMES } from '../constants';
describe('components/wrappedSelect', () => {
let instance;
let choicesInstance;
let element;
beforeEach(() => {
choicesInstance = {
config: {
...DEFAULT_CONFIG,
},
};
element = document.createElement('select');
element.id = 'target';
for (let i = 1; i <= 4; i++) {
@ -32,7 +25,10 @@ describe('components/wrappedSelect', () => {
}
document.body.appendChild(element);
instance = new WrappedSelect(choicesInstance, document.getElementById('target'), DEFAULT_CLASSNAMES);
instance = new WrappedSelect({
element: document.getElementById('target'),
classNames: DEFAULT_CLASSNAMES,
});
});
afterEach(() => {
@ -41,10 +37,6 @@ describe('components/wrappedSelect', () => {
});
describe('constructor', () => {
it('assigns choices instance to class', () => {
expect(instance.parentInstance).to.eql(choicesInstance);
});
it('assigns choices element to class', () => {
expect(instance.element).to.eql(element);
});