diff --git a/README.md b/README.md index a821b50..18b9354 100644 --- a/README.md +++ b/README.md @@ -147,6 +147,7 @@ Or include Choices directly: openState: 'is-open', disabledState: 'is-disabled', highlightedState: 'is-highlighted', + selectedState: 'is-selected', flippedState: 'is-flipped', loadingState: 'is-loading', noResults: 'has-no-results', @@ -578,6 +579,7 @@ classNames: { openState: 'is-open', disabledState: 'is-disabled', highlightedState: 'is-highlighted', + selectedState: 'is-selected', flippedState: 'is-flipped', selectedState: 'is-highlighted', } diff --git a/src/scripts/constants.js b/src/scripts/constants.js index 1f3306a..5718c1c 100644 --- a/src/scripts/constants.js +++ b/src/scripts/constants.js @@ -28,6 +28,7 @@ export const DEFAULT_CLASSNAMES = { openState: 'is-open', disabledState: 'is-disabled', highlightedState: 'is-highlighted', + selectedState: 'is-selected', flippedState: 'is-flipped', loadingState: 'is-loading', noResults: 'has-no-results', diff --git a/src/scripts/constants.test.js b/src/scripts/constants.test.js index 1accbcd..3579dcb 100644 --- a/src/scripts/constants.test.js +++ b/src/scripts/constants.test.js @@ -35,6 +35,7 @@ describe('constants', () => { 'openState', 'disabledState', 'highlightedState', + 'selectedState', 'flippedState', 'loadingState', 'noResults', diff --git a/src/scripts/templates.js b/src/scripts/templates.js index b955200..eb4f2ea 100644 --- a/src/scripts/templates.js +++ b/src/scripts/templates.js @@ -2,9 +2,22 @@ * Helpers to create HTML elements used by Choices * Can be overridden by providing `callbackOnCreateTemplates` option * @typedef {import('../../types/index').Choices.Templates} Templates + * @typedef {import('../../types/index').Choices.ClassNames} ClassNames + * @typedef {import('../../types/index').Choices.Options} Options + * @typedef {import('../../types/index').Choices.Item} Item + * @typedef {import('../../types/index').Choices.Choice} Choice + * @typedef {import('../../types/index').Choices.Group} Group */ export const TEMPLATES = /** @type {Templates} */ ({ + /** + * @param {Partial} classNames + * @param {"ltr" | "rtl" | "auto"} dir + * @param {boolean} isSelectElement + * @param {boolean} isSelectOneElement + * @param {boolean} searchEnabled + * @param {"select-one" | "select-multiple" | "text"} passedElementType + */ containerOuter( { containerOuter }, dir, @@ -40,18 +53,29 @@ export const TEMPLATES = /** @type {Templates} */ ({ return div; }, + /** + * @param {Partial} classNames + */ containerInner({ containerInner }) { return Object.assign(document.createElement('div'), { className: containerInner, }); }, + /** + * @param {Partial} classNames + * @param {boolean} isSelectOneElement + */ itemList({ list, listSingle, listItems }, isSelectOneElement) { return Object.assign(document.createElement('div'), { className: `${list} ${isSelectOneElement ? listSingle : listItems}`, }); }, + /** + * @param {Partial} classNames + * @param {string} value + */ placeholder({ placeholder }, value) { return Object.assign(document.createElement('div'), { className: placeholder, @@ -59,6 +83,11 @@ export const TEMPLATES = /** @type {Templates} */ ({ }); }, + /** + * @param {Partial} classNames + * @param {Item} item + * @param {boolean} removeItemButton + */ item( { item, button, highlightedState, itemSelectable, placeholder }, { @@ -122,6 +151,10 @@ export const TEMPLATES = /** @type {Templates} */ ({ return div; }, + /** + * @param {Partial} classNames + * @param {boolean} isSelectOneElement + */ choiceList({ list }, isSelectOneElement) { const div = Object.assign(document.createElement('div'), { className: list, @@ -135,6 +168,10 @@ export const TEMPLATES = /** @type {Templates} */ ({ return div; }, + /** + * @param {Partial} classNames + * @param {Group} group + */ choiceGroup({ group, groupHeading, itemDisabled }, { id, value, disabled }) { const div = Object.assign(document.createElement('div'), { className: `${group} ${disabled ? itemDisabled : ''}`, @@ -162,15 +199,28 @@ export const TEMPLATES = /** @type {Templates} */ ({ return div; }, + /** + * @param {Partial} classNames + * @param {Choice} choice + * @param {Options['itemSelectText']} selectText + */ choice( - { item, itemChoice, itemSelectable, itemDisabled, placeholder }, + { + item, + itemChoice, + itemSelectable, + selectedState, + itemDisabled, + placeholder, + }, { id, value, label, groupId, elementId, - disabled, + disabled: isDisabled, + selected: isSelected, placeholder: isPlaceholder, }, selectText, @@ -178,11 +228,17 @@ export const TEMPLATES = /** @type {Templates} */ ({ const div = Object.assign(document.createElement('div'), { id: elementId, innerHTML: label, - className: `${item} ${itemChoice} ${ - disabled ? itemDisabled : itemSelectable - } ${isPlaceholder ? placeholder : ''}`, + className: `${item} ${itemChoice}`, }); + if (isSelected) { + div.classList.add(selectedState); + } + + if (isPlaceholder) { + div.classList.add(placeholder); + } + div.setAttribute('role', groupId > 0 ? 'treeitem' : 'option'); Object.assign(div.dataset, { @@ -192,16 +248,22 @@ export const TEMPLATES = /** @type {Templates} */ ({ selectText, }); - if (disabled) { + if (isDisabled) { + div.classList.add(itemDisabled); div.dataset.choiceDisabled = ''; div.setAttribute('aria-disabled', 'true'); } else { + div.classList.add(itemSelectable); div.dataset.choiceSelectable = ''; } return div; }, + /** + * @param {Partial} classNames + * @param {string} placeholderValue + */ input({ input, inputCloned }, placeholderValue) { const inp = Object.assign(document.createElement('input'), { type: 'text', @@ -218,6 +280,9 @@ export const TEMPLATES = /** @type {Templates} */ ({ return inp; }, + /** + * @param {Partial} classNames + */ dropdown({ list, listDropdown }) { const div = document.createElement('div'); @@ -227,6 +292,12 @@ export const TEMPLATES = /** @type {Templates} */ ({ return div; }, + /** + * + * @param {Partial} classNames + * @param {string} innerHTML + * @param {"no-choices" | "no-results" | ""} type + */ notice({ item, itemChoice, noResults, noChoices }, innerHTML, type = '') { const classes = [item, itemChoice]; @@ -242,6 +313,9 @@ export const TEMPLATES = /** @type {Templates} */ ({ }); }, + /** + * @param {Item} option + */ option({ label, value, customProperties, active, disabled }) { const opt = new Option(label, value, false, active); diff --git a/src/scripts/templates.test.js b/src/scripts/templates.test.js index 702d32a..7ed108a 100644 --- a/src/scripts/templates.test.js +++ b/src/scripts/templates.test.js @@ -24,7 +24,7 @@ function expectEqualElements(element1, element2) { describe('templates', () => { describe('containerOuter', () => { const classes = { - containerOuter: 'test', + containerOuter: 'class-1', }; const direction = 'rtl'; @@ -158,7 +158,7 @@ describe('templates', () => { describe('containerInner', () => { it('returns expected html', () => { const classes = { - containerInner: 'test', + containerInner: 'class-1', }; const expectedOutput = strToEl( `
`, @@ -171,9 +171,9 @@ describe('templates', () => { describe('itemList', () => { const classes = { - list: 'test 1', - listSingle: 'test 2', - listItems: 'test 3', + list: 'class-1', + listSingle: 'class-2', + listItems: 'class-3', }; describe('select one element', () => { @@ -202,7 +202,7 @@ describe('templates', () => { describe('placeholder', () => { it('returns expected html', () => { const classes = { - placeholder: 'test', + placeholder: 'class-1', }; const value = 'test'; const expectedOutput = strToEl(` @@ -215,7 +215,7 @@ describe('templates', () => { describe('choiceList', () => { const classes = { - list: 'test', + list: 'class-1', }; describe('select one element', () => { @@ -252,9 +252,9 @@ describe('templates', () => { describe('choiceGroup', () => { const classes = { - group: 'test 1', - groupHeading: 'test 2', - itemDisabled: 'test 3', + group: 'class-1', + groupHeading: 'class-2', + itemDisabled: 'class-3', }; let data; @@ -316,11 +316,12 @@ describe('templates', () => { describe('choice', () => { const classes = { - item: 'test 1', - itemChoice: 'test 2', - itemDisabled: 'test 3', - itemSelectable: 'test 4', - placeholder: 'test 5', + item: 'class-1', + itemChoice: 'class-2', + itemDisabled: 'class-3', + itemSelectable: 'class-4', + placeholder: 'class-5', + selectedState: 'class-6', }; const itemSelectText = 'test 6'; @@ -356,6 +357,8 @@ describe('templates', () => { `); const actualOutput = templates.choice(classes, data, itemSelectText); + console.log(actualOutput); + expectEqualElements(actualOutput, expectedOutput); }); }); @@ -451,8 +454,8 @@ describe('templates', () => { describe('input', () => { const classes = { - input: 'test 1', - inputCloned: 'test 2', + input: 'class-1', + inputCloned: 'class-2', }; it('returns expected html', () => { @@ -479,9 +482,10 @@ describe('templates', () => { describe('dropdown', () => { const classes = { - list: 'test-1', - listDropdown: 'test-2', + list: 'class-1', + listDropdown: 'class-2', }; + it('returns expected html', () => { const value = 'test'; const expectedOutput = strToEl( @@ -495,10 +499,10 @@ describe('templates', () => { describe('notice', () => { const classes = { - item: 'test-1', - itemChoice: 'test-2', - noResults: 'test-3', - noChoices: 'test-4', + item: 'class-1', + itemChoice: 'class-2', + noResults: 'class-3', + noChoices: 'class-4', }; const label = 'test'; diff --git a/types/index.d.ts b/types/index.d.ts index fa37744..abe198d 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -266,6 +266,8 @@ declare namespace Choices { disabledState: string; /** @default 'is-highlighted' */ highlightedState: string; + /** @default 'is-selected' */ + selectedState: string; /** @default 'is-flipped' */ flippedState: string; /** @default 'is-loading' */