mirror of
https://github.com/Choices-js/Choices.git
synced 2024-05-03 14:23:12 +02:00
replicated PR 525 from main repo
This commit is contained in:
parent
5dbea2825a
commit
04977bae97
10
README.md
10
README.md
|
@ -122,6 +122,7 @@ Or include Choices directly:
|
|||
choices: [],
|
||||
renderChoiceLimit: -1,
|
||||
maxItemCount: -1,
|
||||
addChoices: false,
|
||||
addItems: true,
|
||||
addItemFilter: null,
|
||||
removeItems: true,
|
||||
|
@ -304,6 +305,15 @@ Pass an array of objects:
|
|||
|
||||
**Usage:** The amount of items a user can input/select ("-1" indicates no limit).
|
||||
|
||||
### 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`
|
||||
|
||||
### addItems
|
||||
|
||||
**Type:** `Boolean` **Default:** `true`
|
||||
|
|
|
@ -1006,6 +1006,24 @@ 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);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1145,5 +1145,26 @@ describe('Choices - select one', () => {
|
|||
.should('have.length', 3);
|
||||
});
|
||||
});
|
||||
|
||||
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);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -397,6 +397,15 @@
|
|||
multiple
|
||||
></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>
|
||||
|
@ -622,6 +631,8 @@
|
|||
},
|
||||
],
|
||||
});
|
||||
|
||||
new Choices('#choices-add-choices', { addChoices: true });
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
|
|
@ -417,6 +417,16 @@
|
|||
<button class="destroy">Destroy</button>
|
||||
<button class="init">Init</button>
|
||||
</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>
|
||||
|
@ -676,6 +686,8 @@
|
|||
document.querySelector('button.init').addEventListener('click', () => {
|
||||
newDestroyInitChoices.init();
|
||||
});
|
||||
|
||||
new Choices('#choices-add-choices', { addChoices: true });
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
|
|
@ -825,12 +825,12 @@ class Choices implements Choices {
|
|||
);
|
||||
}
|
||||
|
||||
// If we have choices to show
|
||||
const { activeItems } = this._store; // If we have choices to show
|
||||
|
||||
if (
|
||||
choiceListFragment.childNodes &&
|
||||
choiceListFragment.childNodes.length > 0
|
||||
) {
|
||||
const { activeItems } = this._store;
|
||||
const canAddItem = this._canAddItem(activeItems, this.input.value);
|
||||
|
||||
// ...and we can select them
|
||||
|
@ -844,18 +844,21 @@ class Choices implements Choices {
|
|||
}
|
||||
} else {
|
||||
// Otherwise show a notice
|
||||
let dropdownItem;
|
||||
let notice;
|
||||
const canAddChoice = this._canAddChoice(activeItems, this.input.value);
|
||||
|
||||
if (this._isSearching) {
|
||||
notice =
|
||||
let dropdownItem;
|
||||
|
||||
if (canAddChoice.response) {
|
||||
dropdownItem = this._getTemplate('notice', canAddChoice.notice);
|
||||
} else if (this._isSearching) {
|
||||
const notice =
|
||||
typeof this.config.noResultsText === 'function'
|
||||
? this.config.noResultsText()
|
||||
: this.config.noResultsText;
|
||||
|
||||
dropdownItem = this._getTemplate('notice', notice, 'no-results');
|
||||
} else {
|
||||
notice =
|
||||
const notice =
|
||||
typeof this.config.noChoicesText === 'function'
|
||||
? this.config.noChoicesText()
|
||||
: this.config.noChoicesText;
|
||||
|
@ -1260,6 +1263,14 @@ class Choices implements Choices {
|
|||
}
|
||||
}
|
||||
|
||||
_canAddChoice(activeItems: Item[], value: string): Notice {
|
||||
const canAddItem = this._canAddItem(activeItems, value);
|
||||
|
||||
canAddItem.response = this.config.addChoices && canAddItem.response;
|
||||
|
||||
return canAddItem;
|
||||
}
|
||||
|
||||
_canAddItem(activeItems: Item[], value: string): Notice {
|
||||
let canAddItem = true;
|
||||
let notice =
|
||||
|
@ -1555,16 +1566,22 @@ class Choices implements Choices {
|
|||
const { ENTER_KEY: enterKey } = KEY_CODES;
|
||||
const targetWasButton =
|
||||
target && (target as HTMLElement).hasAttribute('data-button');
|
||||
let addedItem = false;
|
||||
|
||||
if (this._isTextElement && target && (target as HTMLInputElement).value) {
|
||||
if (target && (target as HTMLInputElement).value) {
|
||||
const { value } = this.input;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1579,11 +1596,15 @@ class Choices implements 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 {
|
||||
if (activeItems[0]) {
|
||||
// add enter keyCode value
|
||||
activeItems[0].keyCode = enterKey; // eslint-disable-line no-param-reassign
|
||||
}
|
||||
this._handleChoiceAction(activeItems, highlightedChoice);
|
||||
}
|
||||
this._handleChoiceAction(activeItems, highlightedChoice);
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
|
|
|
@ -37,6 +37,7 @@ export const DEFAULT_CONFIG: Options = {
|
|||
silent: false,
|
||||
renderChoiceLimit: -1,
|
||||
maxItemCount: -1,
|
||||
addChoices: false,
|
||||
addItems: true,
|
||||
addItemFilter: null,
|
||||
removeItems: true,
|
||||
|
|
|
@ -102,6 +102,15 @@ export interface Options {
|
|||
*/
|
||||
maxItemCount: number;
|
||||
|
||||
/**
|
||||
* Whether a user can add choices dynamically.
|
||||
*
|
||||
* **Input types affected:** select-one, select-multiple
|
||||
*
|
||||
* @default false
|
||||
*/
|
||||
addChoices: boolean;
|
||||
|
||||
/**
|
||||
* Whether a user can add items.
|
||||
*
|
||||
|
|
Loading…
Reference in a new issue