Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow user-created choices for selects #525

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ will be returned. If you target one element, that instance will be returned.
removeItemButton: false,
editItems: false,
duplicateItemsAllowed: true,
addChoices: false,
delimiter: ',',
paste: true,
searchEnabled: true,
Expand Down Expand Up @@ -275,6 +276,15 @@ Pass an array of objects:

**Usage:** Whether duplicate inputted/chosen items are allowed

### addChoices
**Type**: `Boolean` **Default:** `false`

**Input types affected:** `select-multiple`, `select-one`

**Usage:** Whether a user can add choices

**Note:** `addItems` must also be `true`

### delimiter
**Type:** `String` **Default:** `,`

Expand Down
18 changes: 18 additions & 0 deletions cypress/integration/select-multiple.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -841,5 +841,23 @@ describe('Choices - select multiple', () => {
});
});
});

describe('adding user-created choices', () => {
it('allows the user to add choices', () => {
const newChoice = 'New Choice';

cy.get('[data-test-hook=add-choices]')
.find('.choices__input--cloned')
.type(newChoice)
.type('{enter}');

cy.get('[data-test-hook=add-choices]')
.find('.choices__list--multiple')
.last()
.should($el => {
expect($el).to.contain(newChoice);
});
});
});
});
});
23 changes: 23 additions & 0 deletions cypress/integration/select-one.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -861,5 +861,28 @@ describe('Choices - select one', () => {
});
});
});

describe('adding user-created choices', () => {
beforeEach(() => {
cy.get('[data-test-hook=add-choices]')
.find('.choices')
.click();
});

it('allows the user to add choices', () => {
const newChoice = 'New Choice';

cy.get('[data-test-hook=add-choices]')
.find('.choices__input--cloned')
.type(newChoice)
.type('{enter}');

cy.get('[data-test-hook=add-choices]')
.find('.choices__list--single .choices__item')
.should($el => {
expect($el).to.contain(newChoice);
});
});
});
});
});
45 changes: 34 additions & 11 deletions public/assets/scripts/choices.js
Original file line number Diff line number Diff line change
Expand Up @@ -446,6 +446,7 @@ var DEFAULT_CONFIG = {
removeItemButton: false,
editItems: false,
duplicateItemsAllowed: true,
addChoices: false,
delimiter: ',',
paste: true,
searchEnabled: true,
Expand Down Expand Up @@ -2252,12 +2253,11 @@ function () {
choiceListFragment = this._createGroupsFragment(activeGroups, activeChoices, choiceListFragment);
} else if (activeChoices.length >= 1) {
choiceListFragment = this._createChoicesFragment(activeChoices, choiceListFragment);
} // If we have choices to show
}

var activeItems = this._store.activeItems; // If we have choices to show

if (choiceListFragment.childNodes && choiceListFragment.childNodes.length > 0) {
var activeItems = this._store.activeItems;

var canAddItem = this._canAddItem(activeItems, this.input.value); // ...and we can select them


Expand All @@ -2272,10 +2272,15 @@ function () {
}
} else {
// Otherwise show a notice
var canAddChoice = this._canAddChoice(activeItems, this.input.value);

var dropdownItem;
var notice;

if (this._isSearching) {
if (canAddChoice.response) {
notice = canAddChoice.notice;
dropdownItem = this._getTemplate('notice', notice);
} else if (this._isSearching) {
notice = (0, _utils.isType)('Function', this.config.noResultsText) ? this.config.noResultsText() : this.config.noResultsText;
dropdownItem = this._getTemplate('notice', notice, 'no-results');
} else {
Expand Down Expand Up @@ -2691,6 +2696,16 @@ function () {
notice: notice
};
}
}, {
key: "_canAddChoice",
value: function _canAddChoice(activeItems, value) {
var canAddItem = this._canAddItem(activeItems, value);

return {
response: this.config.addChoices && canAddItem.response,
notice: canAddItem.notice
};
}
}, {
key: "_ajaxCallback",
value: function _ajaxCallback() {
Expand Down Expand Up @@ -2940,13 +2955,16 @@ function () {
hasActiveDropdown = _ref4.hasActiveDropdown;
var enterKey = _constants.KEY_CODES.ENTER_KEY;
var targetWasButton = target.hasAttribute('data-button');
var addedItem;

if (this._isTextElement && target.value) {
if (target.value) {
var value = this.input.value;

var canAddItem = this._canAddItem(activeItems, value);

if (canAddItem.response) {
var canAddChoice = this._canAddChoice(activeItems, value);

if (this._isTextElement && canAddItem.response || !this._isTextElement && canAddChoice.response) {
this.hideDropdown(true);

this._addItem({
Expand All @@ -2956,6 +2974,7 @@ function () {
this._triggerChange(value);

this.clearInput();
addedItem = true;
}
}

Expand All @@ -2969,12 +2988,16 @@ function () {
var highlightedChoice = this.dropdown.getChild(".".concat(this.config.classNames.highlightedState));

if (highlightedChoice) {
// add enter keyCode value
if (activeItems[0]) {
activeItems[0].keyCode = enterKey; // eslint-disable-line no-param-reassign
}
if (addedItem) {
this.unhighlightAll();
} else {
// add enter keyCode value
if (activeItems[0]) {
activeItems[0].keyCode = enterKey; // eslint-disable-line no-param-reassign
}

this._handleChoiceAction(activeItems, highlightedChoice);
this._handleChoiceAction(activeItems, highlightedChoice);
}
}

event.preventDefault();
Expand Down
22 changes: 11 additions & 11 deletions public/assets/scripts/choices.min.js

Large diffs are not rendered by default.

11 changes: 11 additions & 0 deletions public/test/select-multiple.html
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,15 @@ <h2>Select multiple inputs</h2>
<option value="value2">label2</option>
</select>
</div>

<div data-test-hook="add-choices">
<label for="choices-add-choices">Add choices</label>
<select class="form-control" name="choices-add-choices" id="choices-add-choices" multiple>
<option value="Choice 1">Choice 1</option>
<option value="Choice 2">Choice 2</option>
<option value="Choice 3">Choice 3</option>
</select>
</div>
</div>
</div>
<script>
Expand Down Expand Up @@ -334,6 +343,8 @@ <h2>Select multiple inputs</h2>
new Choices('#choices-set-choice-by-value').setChoiceByValue('Choice 2');

new Choices('#choices-search-by-label', { searchFields: ['label'] });

new Choices('#choices-add-choices', { addChoices: true });
});
</script>
</body>
Expand Down
11 changes: 11 additions & 0 deletions public/test/select-one.html
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,15 @@ <h2>Select one inputs</h2>
<option value="value2">label2</option>
</select>
</div>

<div data-test-hook="add-choices">
<label for="choices-add-choices">Add choices</label>
<select class="form-control" name="choices-add-choices" id="choices-add-choices">
<option value="Choice 1">Choice 1</option>
<option value="Choice 2">Choice 2</option>
<option value="Choice 3">Choice 3</option>
</select>
</div>
</div>
</div>
<script>
Expand Down Expand Up @@ -346,6 +355,8 @@ <h2>Select one inputs</h2>
new Choices('#choices-set-choice-by-value').setChoiceByValue('Choice 2');

new Choices('#choices-search-by-label', { searchFields: ['label'] });

new Choices('#choices-add-choices', { addChoices: true });
});
</script>
</body>
Expand Down
41 changes: 33 additions & 8 deletions src/scripts/choices.js
Original file line number Diff line number Diff line change
Expand Up @@ -547,12 +547,13 @@ class Choices {
);
}

const activeItems = this._store.activeItems;

// If we have choices to show
if (
choiceListFragment.childNodes &&
choiceListFragment.childNodes.length > 0
) {
const activeItems = this._store.activeItems;
const canAddItem = this._canAddItem(activeItems, this.input.value);

// ...and we can select them
Expand All @@ -566,10 +567,15 @@ class Choices {
}
} else {
// Otherwise show a notice
const canAddChoice = this._canAddChoice(activeItems, this.input.value);

let dropdownItem;
let notice;

if (this._isSearching) {
if (canAddChoice.response) {
notice = canAddChoice.notice;
dropdownItem = this._getTemplate('notice', notice);
} else if (this._isSearching) {
notice = isType('Function', this.config.noResultsText)
? this.config.noResultsText()
: this.config.noResultsText;
Expand Down Expand Up @@ -993,6 +999,15 @@ class Choices {
};
}

_canAddChoice(activeItems, value) {
const canAddItem = this._canAddItem(activeItems, value);

return {
response: this.config.addChoices && canAddItem.response,
notice: canAddItem.notice,
};
}

_ajaxCallback() {
return (results, value, label) => {
if (!results || !value) {
Expand Down Expand Up @@ -1236,16 +1251,22 @@ class Choices {
_onEnterKey({ event, target, activeItems, hasActiveDropdown }) {
const { ENTER_KEY: enterKey } = KEY_CODES;
const targetWasButton = target.hasAttribute('data-button');
let addedItem;

if (this._isTextElement && target.value) {
if (target.value) {
const value = this.input.value;
const canAddItem = this._canAddItem(activeItems, value);
const canAddChoice = this._canAddChoice(activeItems, value);

if (canAddItem.response) {
if (
(this._isTextElement && canAddItem.response) ||
(!this._isTextElement && canAddChoice.response)
) {
this.hideDropdown(true);
this._addItem({ value });
this._triggerChange(value);
this.clearInput();
addedItem = true;
}
}

Expand All @@ -1260,11 +1281,15 @@ class Choices {
);

if (highlightedChoice) {
// add enter keyCode value
if (activeItems[0]) {
activeItems[0].keyCode = enterKey; // eslint-disable-line no-param-reassign
if (addedItem) {
this.unhighlightAll();
} else {
// add enter keyCode value
if (activeItems[0]) {
activeItems[0].keyCode = enterKey; // eslint-disable-line no-param-reassign
}
this._handleChoiceAction(activeItems, highlightedChoice);
}
this._handleChoiceAction(activeItems, highlightedChoice);
}

event.preventDefault();
Expand Down
1 change: 1 addition & 0 deletions src/scripts/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ export const DEFAULT_CONFIG = {
removeItemButton: false,
editItems: false,
duplicateItemsAllowed: true,
addChoices: false,
delimiter: ',',
paste: true,
searchEnabled: true,
Expand Down