diff --git a/packages/cfpb-atomic-component/src/utilities/atomic-helpers.js b/packages/cfpb-atomic-component/src/utilities/atomic-helpers.js index a06102dba5..923d5ceae3 100755 --- a/packages/cfpb-atomic-component/src/utilities/atomic-helpers.js +++ b/packages/cfpb-atomic-component/src/utilities/atomic-helpers.js @@ -102,10 +102,11 @@ function setInitFlag(element) { * @param {string} selector - Selector to search for in the document. * @param {Function} Constructor - A constructor function. * @param {HTMLElement} [scope] - A dom node in which to query the selector. + * @param {object} config - Configuration will be provided to the Constructor's init() * If not supplied, it defaults to the `document`. * @returns {Array} List of instances that were instantiated. */ -function instantiateAll(selector, Constructor, scope) { +function instantiateAll(selector, Constructor, scope, config = {}) { const base = scope || document; const elements = base.querySelectorAll(selector); const insts = []; @@ -115,7 +116,7 @@ function instantiateAll(selector, Constructor, scope) { element = elements[i]; if (contains(element, INIT_FLAG) === false) { inst = new Constructor(element); - inst.init(); + inst.init(config); insts.push(inst); } } diff --git a/packages/cfpb-forms/src/organisms/Multiselect.js b/packages/cfpb-forms/src/organisms/Multiselect.js index 38118d5f37..afa41b0b85 100644 --- a/packages/cfpb-forms/src/organisms/Multiselect.js +++ b/packages/cfpb-forms/src/organisms/Multiselect.js @@ -5,7 +5,7 @@ import { isMobileUserAgent, instantiateAll, } from '@cfpb/cfpb-atomic-component'; -import MultiselectModel from './MultiselectModel.js'; +import MultiselectModel, { MAX_SELECTIONS } from './MultiselectModel.js'; import { create } from './MultiselectUtils.js'; import * as closeIconSrc from '@cfpb/cfpb-icons/src/icons/error.svg'; @@ -27,6 +27,12 @@ const KEY_UP = 'ArrowUp'; const KEY_DOWN = 'ArrowDown'; const KEY_TAB = 'Tab'; +// Configuration default +const DEFAULT_CONFIG = { + renderTags: true, // Allow the Multiselect to generate the Tag elements in the DOM + maxSelections: MAX_SELECTIONS, // Maximum number of options a user can select +}; + /** * Multiselect * @class @@ -49,6 +55,7 @@ function Multiselect(element) { let _placeholder; let _model; let _options; + let _config; // Multiselect configuration object // Markup elems, convert this to templating engine in the future. let _containerDom; @@ -285,10 +292,13 @@ function Multiselect(element) { const dataOptionSel = '[data-option="' + option.value + '"]'; const _selectionsItemDom = _selectionsDom.querySelector(dataOptionSel); - if (typeof _selectionsItemDom !== 'undefined') { - _selectionsDom.removeChild(_selectionsItemDom); + // If the exists + if (typeof _selectionsItemDom !== 'undefined' && _selectionsItemDom) { + _selectionsDom?.removeChild(_selectionsItemDom); } - } else { + } + // Else, if we are configured to display s then render them + else if (_config?.renderTags && _selectionsDom) { _createSelectedItem(_selectionsDom, option); } _model.toggleOption(optionIndex); @@ -512,7 +522,8 @@ function Multiselect(element) { _optionItemDoms.push(optionsItemDom); - if (isChecked) { + // Create if enabled + if (isChecked && _config?.renderTags) { _createSelectedItem(_selectionsDom, option); } } @@ -527,9 +538,10 @@ function Multiselect(element) { /** * Set up and create the multiselect. + * @param {object} multiselectConfig - Multiselect configuration options * @returns {Multiselect} An instance. */ - function init() { + function init(multiselectConfig = DEFAULT_CONFIG) { if (!setInitFlag(_dom)) { return this; } @@ -543,8 +555,12 @@ function Multiselect(element) { _placeholder = _dom.getAttribute('placeholder'); _options = _dom.options || []; + // Allow devs to pass the config settings they want and not worry about the rest + _config = { ...DEFAULT_CONFIG, ...multiselectConfig }; + if (_options.length > 0) { - _model = new MultiselectModel(_options, _name).init(); + // Store underlying model so we can expose it externally + _model = new MultiselectModel(_options, _name, _config).init(); const newDom = _populateMarkup(); /* Removes element. * @param {string} name - a unique name for this multiselect. + * @param {object} config - Customization of Multiselect behavior */ -function MultiselectModel(options, name) { +function MultiselectModel(options, name, config) { const _options = options; const _name = name; + const _max = config?.maxSelections || MAX_SELECTIONS; + let _optionsData = []; let _selectedIndices = []; @@ -59,7 +62,7 @@ function MultiselectModel(options, name) { * True if the maximum number of options are checked, false otherwise. */ function isAtMaxSelections() { - return _selectedIndices.length === MAX_SELECTIONS; + return _selectedIndices.length >= _max; } /** @@ -108,10 +111,7 @@ function MultiselectModel(options, name) { function toggleOption(index) { _optionsData[index].checked = !_optionsData[index].checked; - if ( - _selectedIndices.length < MAX_SELECTIONS && - _optionsData[index].checked - ) { + if (_selectedIndices.length < _max && _optionsData[index].checked) { _selectedIndices.push(index); _selectedIndices.sort(); diff --git a/packages/cfpb-forms/src/organisms/multiselect.less b/packages/cfpb-forms/src/organisms/multiselect.less index 033f2f1401..fab69972b7 100644 --- a/packages/cfpb-forms/src/organisms/multiselect.less +++ b/packages/cfpb-forms/src/organisms/multiselect.less @@ -132,7 +132,7 @@ select.o-multiselect { pointer-events: none; &::after { - content: 'Reached maximum of five selections'; + content: 'Reached maximum number of selections'; } }