Rename rendering methods + createGroupsFragment test

This commit is contained in:
Josh Johnson 2017-12-20 15:10:01 +00:00
parent a5277a49e7
commit 5048379e68
11 changed files with 326 additions and 86 deletions

View file

@ -48,6 +48,8 @@ global.navigator = {
global.CustomEvent = window.CustomEvent; global.CustomEvent = window.CustomEvent;
global.HTMLElement = window.HTMLElement; global.HTMLElement = window.HTMLElement;
global.HTMLOptionElement = window.HTMLOptionElement; global.HTMLOptionElement = window.HTMLOptionElement;
global.HTMLOptGroupElement = window.HTMLOptGroupElement;
global.DocumentFragment = window.DocumentFragment;
copyProps(window, global); copyProps(window, global);
mockRAF(global); mockRAF(global);

View file

@ -2085,8 +2085,8 @@ var Choices = function () {
*/ */
}, { }, {
key: 'renderGroups', key: 'createGroupsFragment',
value: function renderGroups(groups, choices, fragment) { value: function createGroupsFragment(groups, choices, fragment) {
var _this = this; var _this = this;
var groupFragment = fragment || document.createDocumentFragment(); var groupFragment = fragment || document.createDocumentFragment();
@ -2111,7 +2111,7 @@ var Choices = function () {
if (groupChoices.length >= 1) { if (groupChoices.length >= 1) {
var dropdownGroup = _this._getTemplate('choiceGroup', group); var dropdownGroup = _this._getTemplate('choiceGroup', group);
groupFragment.appendChild(dropdownGroup); groupFragment.appendChild(dropdownGroup);
_this.renderChoices(groupChoices, groupFragment, true); _this.createChoicesFragment(groupChoices, groupFragment, true);
} }
}); });
@ -2127,8 +2127,8 @@ var Choices = function () {
*/ */
}, { }, {
key: 'renderChoices', key: 'createChoicesFragment',
value: function renderChoices(choices, fragment) { value: function createChoicesFragment(choices, fragment) {
var _this2 = this; var _this2 = this;
var withinGroup = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false; var withinGroup = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
@ -2207,8 +2207,8 @@ var Choices = function () {
*/ */
}, { }, {
key: 'renderItems', key: 'createItemsFragment',
value: function renderItems(items) { value: function createItemsFragment(items) {
var _this3 = this; var _this3 = this;
var fragment = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null; var fragment = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
@ -2300,11 +2300,11 @@ var Choices = function () {
return activeChoice.placeholder === true && activeChoice.groupId === -1; return activeChoice.placeholder === true && activeChoice.groupId === -1;
}); });
if (activePlaceholders.length >= 1) { if (activePlaceholders.length >= 1) {
choiceListFragment = this.renderChoices(activePlaceholders, choiceListFragment); choiceListFragment = this.createChoicesFragment(activePlaceholders, choiceListFragment);
} }
choiceListFragment = this.renderGroups(activeGroups, activeChoices, choiceListFragment); choiceListFragment = this.createGroupsFragment(activeGroups, activeChoices, choiceListFragment);
} else if (activeChoices.length >= 1) { } else if (activeChoices.length >= 1) {
choiceListFragment = this.renderChoices(activeChoices, choiceListFragment); choiceListFragment = this.createChoicesFragment(activeChoices, choiceListFragment);
} }
var _activeItems = this.store.getItemsFilteredByActive(); var _activeItems = this.store.getItemsFilteredByActive();
@ -2350,7 +2350,7 @@ var Choices = function () {
if (activeItems.length) { if (activeItems.length) {
// Create a fragment to store our list items // Create a fragment to store our list items
// (so we don't have to update the DOM for each item) // (so we don't have to update the DOM for each item)
var itemListFragment = this.renderItems(activeItems); var itemListFragment = this.createItemsFragment(activeItems);
// If we have items to add // If we have items to add
if (itemListFragment.childNodes) { if (itemListFragment.childNodes) {

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -282,9 +282,8 @@ class Choices {
* @return {DocumentFragment} Populated options fragment * @return {DocumentFragment} Populated options fragment
* @private * @private
*/ */
renderGroups(groups, choices, fragment) { createGroupsFragment(groups, choices, fragment) {
const groupFragment = fragment || document.createDocumentFragment(); const groupFragment = fragment || document.createDocumentFragment();
const filter = this.config.sortFn;
const getGroupChoices = group => choices.filter((choice) => { const getGroupChoices = group => choices.filter((choice) => {
if (this.isSelectOneElement) { if (this.isSelectOneElement) {
return choice.groupId === group.id; return choice.groupId === group.id;
@ -292,18 +291,18 @@ class Choices {
return choice.groupId === group.id && (this.config.renderSelectedChoices === 'always' || !choice.selected); return choice.groupId === group.id && (this.config.renderSelectedChoices === 'always' || !choice.selected);
}); });
// If sorting is enabled, filter groups // If sorting is enabled, filter groups
if (this.config.shouldSort) { if (this.config.shouldSort) {
groups.sort(filter); groups.sort(this.config.sortFn);
} }
groups.forEach((group) => { groups.forEach((group) => {
const groupChoices = getGroupChoices(group); const groupChoices = getGroupChoices(group);
if (groupChoices.length >= 1) { if (groupChoices.length >= 1) {
const dropdownGroup = this._getTemplate('choiceGroup', group); const dropdownGroup = this._getTemplate('choiceGroup', group);
groupFragment.appendChild(dropdownGroup); groupFragment.appendChild(dropdownGroup);
this.renderChoices(groupChoices, groupFragment, true); this.createChoicesFragment(groupChoices, groupFragment, true);
} }
}); });
@ -317,7 +316,7 @@ class Choices {
* @return {DocumentFragment} Populated choices fragment * @return {DocumentFragment} Populated choices fragment
* @private * @private
*/ */
renderChoices(choices, fragment, withinGroup = false) { createChoicesFragment(choices, fragment, withinGroup = false) {
// Create a fragment to store our list items (so we don't have to update the DOM for each item) // Create a fragment to store our list items (so we don't have to update the DOM for each item)
const choicesFragment = fragment || document.createDocumentFragment(); const choicesFragment = fragment || document.createDocumentFragment();
const { renderSelectedChoices, searchResultLimit, renderChoiceLimit } = this.config; const { renderSelectedChoices, searchResultLimit, renderChoiceLimit } = this.config;
@ -381,7 +380,7 @@ class Choices {
* @return * @return
* @private * @private
*/ */
renderItems(items, fragment = null) { createItemsFragment(items, fragment = null) {
// Create fragment to add elements to // Create fragment to add elements to
const itemListFragment = fragment || document.createDocumentFragment(); const itemListFragment = fragment || document.createDocumentFragment();
@ -452,11 +451,11 @@ class Choices {
activeChoice => activeChoice.placeholder === true && activeChoice.groupId === -1, activeChoice => activeChoice.placeholder === true && activeChoice.groupId === -1,
); );
if (activePlaceholders.length >= 1) { if (activePlaceholders.length >= 1) {
choiceListFragment = this.renderChoices(activePlaceholders, choiceListFragment); choiceListFragment = this.createChoicesFragment(activePlaceholders, choiceListFragment);
} }
choiceListFragment = this.renderGroups(activeGroups, activeChoices, choiceListFragment); choiceListFragment = this.createGroupsFragment(activeGroups, activeChoices, choiceListFragment);
} else if (activeChoices.length >= 1) { } else if (activeChoices.length >= 1) {
choiceListFragment = this.renderChoices(activeChoices, choiceListFragment); choiceListFragment = this.createChoicesFragment(activeChoices, choiceListFragment);
} }
// If we have choices to show // If we have choices to show
@ -506,7 +505,7 @@ class Choices {
if (activeItems.length) { if (activeItems.length) {
// Create a fragment to store our list items // Create a fragment to store our list items
// (so we don't have to update the DOM for each item) // (so we don't have to update the DOM for each item)
const itemListFragment = this.renderItems(activeItems); const itemListFragment = this.createItemsFragment(activeItems);
// If we have items to add, append them // If we have items to add, append them
if (itemListFragment.childNodes) { if (itemListFragment.childNodes) {

View file

@ -94,9 +94,9 @@ describe('Choices', () => {
expect(instance.init).to.be.a('function'); expect(instance.init).to.be.a('function');
expect(instance.destroy).to.be.a('function'); expect(instance.destroy).to.be.a('function');
expect(instance.render).to.be.a('function'); expect(instance.render).to.be.a('function');
expect(instance.renderGroups).to.be.a('function'); expect(instance.createGroupsFragment).to.be.a('function');
expect(instance.renderItems).to.be.a('function'); expect(instance.createItemsFragment).to.be.a('function');
expect(instance.renderChoices).to.be.a('function'); expect(instance.createChoicesFragment).to.be.a('function');
expect(instance.highlightItem).to.be.a('function'); expect(instance.highlightItem).to.be.a('function');
expect(instance.unhighlightItem).to.be.a('function'); expect(instance.unhighlightItem).to.be.a('function');
expect(instance.highlightAll).to.be.a('function'); expect(instance.highlightAll).to.be.a('function');

View file

@ -1483,93 +1483,239 @@ describe('choices', () => {
}); });
}); });
describe('renderGroups', () => { describe('createGroupsFragment', () => {
let renderChoicesStub; let createChoicesFragmentStub;
const choices = [ const choices = [
{ {
id: 1, id: 1,
selected: true, selected: true,
groupId: 1, groupId: 1,
}, value: 'Choice 1',
{ label: 'Choice 1',
id: 1,
selected: false,
groupId: 2,
}, },
{ {
id: 2, id: 2,
selected: false, selected: false,
groupId: 2,
value: 'Choice 2',
label: 'Choice 2',
},
{
id: 3,
selected: false,
groupId: 1, groupId: 1,
value: 'Choice 3',
label: 'Choice 3',
}, },
]; ];
const groups = [ const groups = [
{
id: 1,
value: 'Group 1',
active: true,
disabled: false,
},
{ {
id: 2, id: 2,
value: 'Group 2', value: 'Group 2',
active: true, active: true,
disabled: false, disabled: false,
}, },
{
id: 1,
value: 'Group 1',
active: true,
disabled: false,
},
]; ];
beforeEach(() => { beforeEach(() => {
renderChoicesStub = stub(); createChoicesFragmentStub = stub();
instance.renderChoices = renderChoicesStub; instance.createChoicesFragment = createChoicesFragmentStub;
});
afterEach(() => {
instance.createChoicesFragment.reset();
}); });
describe('returning a fragment of groups', () => { describe('returning a fragment of groups', () => {
describe('passing fragment argument', () => { describe('passing fragment argument', () => {
it('updates fragment with groups', () => { it('updates fragment with groups', () => {
const fragment = document.createDocumentFragment();
const childElement = document.createElement('div');
fragment.appendChild(childElement);
output = instance.createGroupsFragment(groups, choices, fragment);
const elementToWrapFragment = document.createElement('div');
elementToWrapFragment.appendChild(output);
expect(output).to.be.instanceOf(DocumentFragment);
expect(elementToWrapFragment.children[0]).to.eql(childElement);
expect(elementToWrapFragment.querySelectorAll('[data-group]').length).to.equal(2);
}); });
}); });
describe('not passing fragment argument', () => { describe('not passing fragment argument', () => {
it('returns new groups fragment', () => { it('returns new groups fragment', () => {
output = instance.createGroupsFragment(groups, choices);
const elementToWrapFragment = document.createElement('div');
elementToWrapFragment.appendChild(output);
}); expect(output).to.be.instanceOf(DocumentFragment);
}); expect(elementToWrapFragment.querySelectorAll('[data-group]').length).to.equal(2);
describe('select-one element', () => {
it('renders group choices', () => {
});
});
describe('text/select-multiple element', () => {
describe('renderSelectedChoices set to true', () => {
it('renders group choices', () => {
});
});
describe('renderSelectedChoices set to false', () => {
it('renders group choices that are not already selected', () => {
});
}); });
}); });
describe('sorting groups', () => { describe('sorting groups', () => {
it('returns groups fragment sorted by config.sortFn', () => { let sortFnStub;
beforeEach(() => {
sortFnStub = stub();
instance.config.sortFn = sortFnStub;
instance.config.shouldSort = true;
});
afterEach(() => {
instance.config.sortFn.reset();
});
it('sorts groups by config.sortFn', () => {
expect(sortFnStub.called).to.equal(false);
instance.createGroupsFragment(groups, choices);
expect(sortFnStub.called).to.equal(true);
});
});
describe('not sorting groups', () => {
let sortFnStub;
beforeEach(() => {
sortFnStub = stub();
instance.config.sortFn = sortFnStub;
instance.config.shouldSort = false;
});
afterEach(() => {
instance.config.sortFn.reset();
});
it('does not sort groups', () => {
instance.createGroupsFragment(groups, choices);
expect(sortFnStub.called).to.equal(false);
});
});
describe('select-one element', () => {
beforeEach(() => {
instance.isSelectOneElement = true;
});
it('calls createChoicesFragment with choices that belong to each group', () => {
expect(createChoicesFragmentStub.called).to.equal(false);
instance.createGroupsFragment(groups, choices);
expect(createChoicesFragmentStub.called).to.equal(true);
expect(createChoicesFragmentStub.firstCall.args[0]).to.eql([
{
id: 1,
selected: true,
groupId: 1,
value: 'Choice 1',
label: 'Choice 1',
},
{
id: 3,
selected: false,
groupId: 1,
value: 'Choice 3',
label: 'Choice 3',
},
]);
expect(createChoicesFragmentStub.secondCall.args[0]).to.eql([
{
id: 2,
selected: false,
groupId: 2,
value: 'Choice 2',
label: 'Choice 2',
},
]);
});
});
describe('text/select-multiple element', () => {
describe('renderSelectedChoices set to "always"', () => {
beforeEach(() => {
instance.isSelectOneElement = false;
instance.config.renderSelectedChoices = 'always';
});
it('calls createChoicesFragment with choices that belong to each group', () => {
expect(createChoicesFragmentStub.called).to.equal(false);
instance.createGroupsFragment(groups, choices);
expect(createChoicesFragmentStub.called).to.equal(true);
expect(createChoicesFragmentStub.firstCall.args[0]).to.eql([
{
id: 1,
selected: true,
groupId: 1,
value: 'Choice 1',
label: 'Choice 1',
},
{
id: 3,
selected: false,
groupId: 1,
value: 'Choice 3',
label: 'Choice 3',
},
]);
expect(createChoicesFragmentStub.secondCall.args[0]).to.eql([
{
id: 2,
selected: false,
groupId: 2,
value: 'Choice 2',
label: 'Choice 2',
},
]);
});
});
describe('renderSelectedChoices not set to "always"', () => {
beforeEach(() => {
instance.isSelectOneElement = false;
instance.config.renderSelectedChoices = false;
});
it('calls createChoicesFragment with choices that belong to each group that are not already selected', () => {
expect(createChoicesFragmentStub.called).to.equal(false);
instance.createGroupsFragment(groups, choices);
expect(createChoicesFragmentStub.called).to.equal(true);
expect(createChoicesFragmentStub.firstCall.args[0]).to.eql([
{
id: 3,
selected: false,
groupId: 1,
value: 'Choice 3',
label: 'Choice 3',
},
]);
expect(createChoicesFragmentStub.secondCall.args[0]).to.eql([
{
id: 2,
selected: false,
groupId: 2,
value: 'Choice 2',
label: 'Choice 2',
},
]);
});
}); });
}); });
}); });
}); });
describe('renderChoices', () => { describe('createChoicesFragment', () => {
beforeEach(() => {}); beforeEach(() => {});
it('returns a fragment of choices', () => {}); it('returns a fragment of choices', () => {});
}); });
describe('renderItems', () => { describe('createItemsFragment', () => {
beforeEach(() => {}); beforeEach(() => {});
it('returns a fragment of items', () => {}); it('returns a fragment of items', () => {});
}); });

View file

@ -5,7 +5,7 @@ import { DEFAULT_CLASSNAMES, DEFAULT_CONFIG } from '../constants';
describe('components/wrappedElement', () => { describe('components/wrappedElement', () => {
let instance; let instance;
let choicesInstance; let choicesInstance;
let choicesElement; let element;
beforeEach(() => { beforeEach(() => {
choicesInstance = { choicesInstance = {
@ -14,8 +14,8 @@ describe('components/wrappedElement', () => {
}, },
}; };
choicesElement = document.createElement('select'); element = document.createElement('select');
instance = new WrappedElement(choicesInstance, choicesElement, DEFAULT_CLASSNAMES); instance = new WrappedElement(choicesInstance, element, DEFAULT_CLASSNAMES);
}); });
afterEach(() => { afterEach(() => {
@ -23,9 +23,27 @@ describe('components/wrappedElement', () => {
instance = null; instance = null;
}); });
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);
});
it('assigns classnames to class', () => {
expect(instance.classNames).to.eql(DEFAULT_CLASSNAMES);
});
it('sets isDisabled flag to false', () => {
expect(instance.isDisabled).to.eql(false);
});
});
describe('getElement', () => { describe('getElement', () => {
it('returns DOM reference of element', () => { it('returns DOM reference of element', () => {
expect(instance.getElement()).to.eql(choicesElement); expect(instance.getElement()).to.eql(element);
}); });
}); });

View file

@ -7,7 +7,7 @@ import { DEFAULT_CLASSNAMES, DEFAULT_CONFIG } from '../constants';
describe('components/wrappedInput', () => { describe('components/wrappedInput', () => {
let instance; let instance;
let choicesInstance; let choicesInstance;
let inputElement; let element;
beforeEach(() => { beforeEach(() => {
choicesInstance = { choicesInstance = {
@ -15,8 +15,8 @@ describe('components/wrappedInput', () => {
...DEFAULT_CONFIG, ...DEFAULT_CONFIG,
}, },
}; };
inputElement = document.createElement('input'); element = document.createElement('input');
instance = new WrappedInput(choicesInstance, inputElement, DEFAULT_CLASSNAMES); instance = new WrappedInput(choicesInstance, element, DEFAULT_CLASSNAMES);
}); });
afterEach(() => { afterEach(() => {
@ -24,6 +24,20 @@ describe('components/wrappedInput', () => {
instance = null; instance = null;
}); });
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);
});
it('assigns classnames to class', () => {
expect(instance.classNames).to.eql(DEFAULT_CLASSNAMES);
});
});
describe('inherited methods', () => { describe('inherited methods', () => {
['getElement', 'conceal', 'reveal', 'enable', 'disable'].forEach((method) => { ['getElement', 'conceal', 'reveal', 'enable', 'disable'].forEach((method) => {
describe(method, () => { describe(method, () => {

View file

@ -7,7 +7,7 @@ import { DEFAULT_CLASSNAMES, DEFAULT_CONFIG } from '../constants';
describe('components/wrappedSelect', () => { describe('components/wrappedSelect', () => {
let instance; let instance;
let choicesInstance; let choicesInstance;
let selectElement; let element;
beforeEach(() => { beforeEach(() => {
choicesInstance = { choicesInstance = {
@ -16,8 +16,8 @@ describe('components/wrappedSelect', () => {
}, },
}; };
selectElement = document.createElement('select'); element = document.createElement('select');
selectElement.id = 'target'; element.id = 'target';
for (let i = 1; i <= 4; i++) { for (let i = 1; i <= 4; i++) {
const option = document.createElement('option'); const option = document.createElement('option');
@ -28,9 +28,9 @@ describe('components/wrappedSelect', () => {
option.setAttribute('placeholder', ''); option.setAttribute('placeholder', '');
} }
selectElement.appendChild(option); element.appendChild(option);
} }
document.body.appendChild(selectElement); document.body.appendChild(element);
instance = new WrappedSelect(choicesInstance, document.getElementById('target'), DEFAULT_CLASSNAMES); instance = new WrappedSelect(choicesInstance, document.getElementById('target'), DEFAULT_CLASSNAMES);
}); });
@ -40,6 +40,20 @@ describe('components/wrappedSelect', () => {
instance = null; instance = null;
}); });
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);
});
it('assigns classnames to class', () => {
expect(instance.classNames).to.eql(DEFAULT_CLASSNAMES);
});
});
describe('inherited methods', () => { describe('inherited methods', () => {
['getElement', 'conceal', 'reveal', 'enable', 'disable'].forEach((method) => { ['getElement', 'conceal', 'reveal', 'enable', 'disable'].forEach((method) => {
beforeEach(() => { beforeEach(() => {
@ -77,15 +91,62 @@ describe('components/wrappedSelect', () => {
}); });
}); });
// describe('getOptionGroups', () => { describe('getOptionGroups', () => {
// it('returns all option groups', () => { it('returns an array of all option groups', () => {
for (let i = 1; i <= 3; i++) {
const group = document.createElement('optgroup');
instance.element.appendChild(group);
}
// }); const output = instance.getOptionGroups();
// }); expect(output.length).to.equal(3);
output.forEach((option) => {
expect(option).to.be.instanceOf(HTMLOptGroupElement);
});
});
});
// describe('setOptions', () => { describe('setOptions', () => {
let appendDocFragmentStub;
const options = [
{
value: '1',
label: 'Test 1',
selected: false,
disabled: true,
},
{
value: '2',
label: 'Test 2',
selected: true,
disabled: false,
},
];
// }); beforeEach(() => {
appendDocFragmentStub = stub();
instance.appendDocFragment = appendDocFragmentStub;
});
afterEach(() => {
instance.appendDocFragment.reset();
});
it('creates an option element for each passed object, adds it to a fragment and calls appendDocFragment with created fragment', () => {
expect(appendDocFragmentStub.called).to.equal(false);
instance.setOptions(options);
expect(appendDocFragmentStub.called).to.equal(true);
const fragment = appendDocFragmentStub.firstCall.args[0];
const selectElement = document.createElement('select');
selectElement.appendChild(fragment);
expect(fragment).to.be.instanceOf(DocumentFragment);
expect(selectElement.options.length).to.equal(2);
expect(selectElement.options[0].value).to.equal(options[0].value);
expect(selectElement.options[1].value).to.equal(options[1].value);
});
});
describe('appendDocFragment', () => { describe('appendDocFragment', () => {
it('empties contents of element', () => { it('empties contents of element', () => {

6
types/index.d.ts vendored
View file

@ -911,13 +911,13 @@ export default class Choices {
ajax(fn: (values: any) => any): this; ajax(fn: (values: any) => any): this;
/** Render group choices into a DOM fragment and append to choice list */ /** Render group choices into a DOM fragment and append to choice list */
private renderGroups(groups: any[], choices: any[], fragment: DocumentFragment): DocumentFragment; private createGroupsFragment(groups: any[], choices: any[], fragment: DocumentFragment): DocumentFragment;
/** Render choices into a DOM fragment and append to choice list */ /** Render choices into a DOM fragment and append to choice list */
private renderChoices(choices: any[], fragment: DocumentFragment, withinGroup?: boolean): DocumentFragment; private createChoicesFragment(choices: any[], fragment: DocumentFragment, withinGroup?: boolean): DocumentFragment;
/** Render items into a DOM fragment and append to items list */ /** Render items into a DOM fragment and append to items list */
private renderItems(items: any[], fragment?: DocumentFragment): void; private createItemsFragment(items: any[], fragment?: DocumentFragment): void;
/** Render DOM with values */ /** Render DOM with values */
private render(): void; private render(): void;