Skip to content

Commit

Permalink
callback to filter items before adding (Choices-js#485)
Browse files Browse the repository at this point in the history
* Add item custom callback

* Minor unit test updates

* Test updates, Changed callback name to more clearly distinguish it's function

* Fix description wording in cypress

* Update README

* Updated filter item callback name to be addItemFilter
  • Loading branch information
gkweb authored and jshjohnson committed Feb 11, 2019
1 parent 23e5e76 commit ba09fb0
Show file tree
Hide file tree
Showing 6 changed files with 80 additions and 0 deletions.
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ will be returned. If you target one element, that instance will be returned.
position: 'auto',
resetScrollPosition: true,
regexFilter: null,
addItemFilter: null,
shouldSort: true,
shouldSortItems: false,
sortFn: () => {...},
Expand Down Expand Up @@ -339,6 +340,24 @@ Pass an array of objects:

**Usage:** Whether the scroll position should reset after adding an item.

### addItemFilter
**Type:** `Function` **Default:** `null`

**Input types affected:** `text`

**Usage:** A callback function that will need to return `true` for a user to successfully add an item.

**Example:**

```js
// Only adds items matching the text test
new Choices(element, {
addItemFilter: function (value) {
return (value !== 'test')
}
});
```

### regexFilter
**Type:** `Regex` **Default:** `null`

Expand Down
33 changes: 33 additions & 0 deletions cypress/integration/text.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,39 @@ describe('Choices - text element', () => {
});
});

describe('custom add item callback', () => {
describe('inputting a value that satisfies the addItemFilter', () => {
const input = 'test';

it('allows me to add choice', () => {
cy.get('[data-test-hook=add-item-callback]')
.find('.choices__input--cloned')
.type(input)
.type('{enter}');

cy.get('[data-test-hook=add-item-callback]')
.find('.choices__list--multiple .choices__item')
.last()
.should($choice => {
expect($choice.text().trim()).to.equal(input);
});
});
});

describe('inputting a value that does not satisfy the callback', () => {
it('displays dropdown prompt', () => {
cy.get('[data-test-hook=add-item-callback]')
.find('.choices__input--cloned')
.type(`this is not the allowed text`)
.type('{enter}');

cy.get('[data-test-hook=add-item-callback]')
.find('.choices__list--dropdown')
.should('not.be.visible');
});
});
});

describe('disabled via attribute', () => {
it('does not allow me to input data', () => {
cy.get('[data-test-hook=disabled-via-attr]')
Expand Down
11 changes: 11 additions & 0 deletions public/test/text.html
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,11 @@ <h2>Text inputs</h2>
<input class="form-control" id="choices-adding-items-disabled" type="text">
</div>

<div data-test-hook="add-item-callback">
<label for="choices-add-item-callback">Callback on Add Item</label>
<input class="form-control" id="choices-add-item-callback" type="text">
</div>

<div data-test-hook="disabled-via-attr">
<label for="choices-disabled-via-attr">Disabled via attribute</label>
<input class="form-control" id="choices-disabled-via-attr" type="text" disabled>
Expand Down Expand Up @@ -120,6 +125,12 @@ <h2>Text inputs</h2>
addItems: false,
});

new Choices('#choices-add-item-callback', {
addItemFilter: function (value) {
return (value !== 'test')
}
});

new Choices('#choices-disabled-via-attr');

new Choices('#choices-prepend-append', {
Expand Down
13 changes: 13 additions & 0 deletions src/scripts/choices.js
Original file line number Diff line number Diff line change
Expand Up @@ -987,6 +987,19 @@ class Choices {
? this.config.uniqueItemText(value)
: this.config.uniqueItemText;
}

if (
isType('Function', this.config.addItemFilter) &&
this.config.addItemFilter(value) &&
this._isTextElement &&
this.config.addItems &&
canAddItem
) {
canAddItem = false;
notice = isType('Function', this.config.customAddItemText)
? this.config.customAddItemText(value)
: this.config.customAddItemText;
}
}

return {
Expand Down
2 changes: 2 additions & 0 deletions src/scripts/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,13 +64,15 @@ export const DEFAULT_CONFIG = {
noChoicesText: 'No choices to choose from',
itemSelectText: 'Press to select',
uniqueItemText: 'Only unique values can be added',
customAddItemText: 'Only values matching specific conditions can be added.',
addItemText: value => `Press Enter to add <b>"${stripHTML(value)}"</b>`,
maxItemText: maxItemCount => `Only ${maxItemCount} values can be added`,
itemComparer: (choice, item) => choice === item,
fuseOptions: {
includeScore: true,
},
callbackOnInit: null,
addItemFilter: null,
callbackOnCreateTemplates: null,
classNames: DEFAULT_CLASSNAMES,
};
Expand Down
2 changes: 2 additions & 0 deletions src/scripts/constants.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,9 +82,11 @@ describe('constants', () => {
expect(DEFAULT_CONFIG.noChoicesText).to.be.a('string');
expect(DEFAULT_CONFIG.itemSelectText).to.be.a('string');
expect(DEFAULT_CONFIG.uniqueItemText).to.be.a('string');
expect(DEFAULT_CONFIG.customAddItemText).to.be.a('string');
expect(DEFAULT_CONFIG.addItemText).to.be.a('function');
expect(DEFAULT_CONFIG.maxItemText).to.be.a('function');
expect(DEFAULT_CONFIG.fuseOptions).to.be.an('object');
expect(DEFAULT_CONFIG.addItemFilter).to.equal(null);
expect(DEFAULT_CONFIG.callbackOnInit).to.equal(null);
expect(DEFAULT_CONFIG.callbackOnCreateTemplates).to.equal(null);
});
Expand Down

0 comments on commit ba09fb0

Please sign in to comment.