From 8679871bfdef07403f8f85d4f6a0dceb03adb359 Mon Sep 17 00:00:00 2001 From: Adam Mockor Date: Wed, 5 Apr 2017 17:11:09 +0200 Subject: [PATCH] placeholder in single select should be always first choice, regardless of sorting --- assets/scripts/src/choices.js | 38 ++++++++++++++++++++++++++++++----- index.html | 3 +-- tests/spec/choices_spec.js | 15 ++++++++++++-- 3 files changed, 47 insertions(+), 9 deletions(-) diff --git a/assets/scripts/src/choices.js b/assets/scripts/src/choices.js index 47d87be..89532fd 100644 --- a/assets/scripts/src/choices.js +++ b/assets/scripts/src/choices.js @@ -312,12 +312,26 @@ class Choices { const choicesFragment = fragment || document.createDocumentFragment(); const filter = this.isSearching ? sortByScore : this.config.sortFilter; + // Split array into placeholedrs and "normal" choices + const { placeholderChoices, normalChoices } = choices.reduce((acc, choice) => { + if (choice.placeholder) { + acc.placeholderChoices.push(choice); + } else { + acc.normalChoices.push(choice); + } + return acc; + }, { placeholderChoices: [], normalChoices: [] }); + // If sorting is enabled or the user is searching, filter choices + // Do not sort placeholder if (this.config.shouldSort || this.isSearching) { - choices.sort(filter); + normalChoices.sort(filter); } - choices.forEach((choice) => { + // Prepend placeholedr + const sortedChoices = [...placeholderChoices, ...normalChoices]; + + sortedChoices.forEach((choice) => { const dropdownItem = this._getTemplate('choice', choice); const shouldRender = this.passedElement.type === 'select-one' || !choice.selected; if (shouldRender) { @@ -2341,18 +2355,32 @@ class Choices { }); }); + // Split array into placeholedrs and "normal" choices + const { placeholderChoices, normalChoices } = allChoices.reduce((acc, choice) => { + if (choice.placeholder) { + acc.placeholderChoices.push(choice); + } else { + acc.normalChoices.push(choice); + } + return acc; + }, { placeholderChoices: [], normalChoices: [] }); + // If sorting is enabled or the user is searching, filter choices + // Do not sort placeholder if (this.config.shouldSort) { - allChoices.sort(filter); + normalChoices.sort(filter); } + // Prepend placeholder + const sortedChoices = [...placeholderChoices, ...normalChoices]; + // Determine whether there is a selected choice - const hasSelectedChoice = allChoices.some((choice) => { + const hasSelectedChoice = sortedChoices.some((choice) => { return choice.selected === true; }); // Add each choice - allChoices.forEach((choice, index) => { + sortedChoices.forEach((choice, index) => { const isDisabled = choice.disabled ? choice.disabled : false; const isSelected = choice.selected ? choice.selected : false; // Pre-select first choice if it's a single select diff --git a/index.html b/index.html index adcd060..90562d5 100644 --- a/index.html +++ b/index.html @@ -146,7 +146,6 @@

Single select input

- + diff --git a/tests/spec/choices_spec.js b/tests/spec/choices_spec.js index 8bf9970..8449290 100644 --- a/tests/spec/choices_spec.js +++ b/tests/spec/choices_spec.js @@ -256,7 +256,6 @@ describe('Choices', () => { beforeEach(function() { this.input = document.createElement('select'); this.input.className = 'js-choices'; - this.input.placeholder = 'Placeholder text'; for (let i = 1; i < 4; i++) { const option = document.createElement('option'); @@ -407,7 +406,7 @@ describe('Choices', () => { expect(showDropdownSpy).toHaveBeenCalled(); }); -it('should trigger hideDropdown on dropdown closing', function() { + it('should trigger hideDropdown on dropdown closing', function() { this.choices = new Choices(this.input); const container = this.choices.containerOuter; @@ -492,6 +491,18 @@ it('should trigger hideDropdown on dropdown closing', function() { expect(this.choices.currentState.choices[0].value).toEqual('Value 1'); }); + + it('should set placeholder as first option if shouldSort true', function() { + const option = document.createElement('option'); + option.setAttribute('selected', ''); + option.setAttribute('placeholder', ''); + option.setAttribute('disabled', ''); + option.innerHTML = 'Placeholder'; + this.input.appendChild(option); + + this.choices = new Choices(this.input, { shouldSort: true }); + expect(this.choices.currentState.choices[0].value).toEqual('Placeholder'); + }); }); describe('should accept multiple select inputs', function() {