mirror of
https://github.com/Choices-js/Choices.git
synced 2024-05-11 10:06:35 +02:00
* Housekeeping * Resolve placeholder bug + hide from choice list * Restructure test folder * Update cypress test to assert one placeholder * Fix breaking e2e test * Remove ability to pass placeholder via config for select boxes * Add further e2e tests covering placeholders * Add unit tests for _generatePlaceholderValue * Display placeholder choice for select one * Add further e2e test to assert on placeholder ordering * Add labels to exclude from draft releases * Add failure case to e2e test workflow * Resolve broken e2e test * Update puppeteer snapshot baseline
This commit is contained in:
parent
939a73b762
commit
a0fe05f926
BIN
.github/actions-scripts/__snapshots__/puppeteer-darwin.png
vendored
Normal file → Executable file
BIN
.github/actions-scripts/__snapshots__/puppeteer-darwin.png
vendored
Normal file → Executable file
Binary file not shown.
Before Width: | Height: | Size: 294 KiB After Width: | Height: | Size: 264 KiB |
3
.github/release-drafter.yml
vendored
3
.github/release-drafter.yml
vendored
|
@ -1,6 +1,9 @@
|
||||||
name-template: 'Draft (next release)'
|
name-template: 'Draft (next release)'
|
||||||
tag-template: 'v$NEXT_PATCH_VERSION'
|
tag-template: 'v$NEXT_PATCH_VERSION'
|
||||||
sort-direction: descending
|
sort-direction: descending
|
||||||
|
exclude-labels:
|
||||||
|
- 'skip-changelog'
|
||||||
|
- 'release'
|
||||||
categories:
|
categories:
|
||||||
- title: '🚨 Breaking changes'
|
- title: '🚨 Breaking changes'
|
||||||
labels:
|
labels:
|
||||||
|
|
17
.github/workflows/e2e-tests.yml
vendored
17
.github/workflows/e2e-tests.yml
vendored
|
@ -29,7 +29,7 @@ jobs:
|
||||||
env:
|
env:
|
||||||
HUSKY_SKIP_INSTALL: true
|
HUSKY_SKIP_INSTALL: true
|
||||||
|
|
||||||
- name: run Cypress CI
|
- name: run Cypress (with recording)
|
||||||
run: npx run-p --race start cypress:ci
|
run: npx run-p --race start cypress:ci
|
||||||
env:
|
env:
|
||||||
CI: true
|
CI: true
|
||||||
|
@ -41,3 +41,18 @@ jobs:
|
||||||
COMMIT_INFO_BRANCH: ${{ github.head_ref }}
|
COMMIT_INFO_BRANCH: ${{ github.head_ref }}
|
||||||
COMMIT_INFO_AUTHOR: ${{ github.event.sender.login }}
|
COMMIT_INFO_AUTHOR: ${{ github.event.sender.login }}
|
||||||
COMMIT_INFO_SHA: ${{ github.event.after }}
|
COMMIT_INFO_SHA: ${{ github.event.after }}
|
||||||
|
|
||||||
|
# if we have ran out of free Cypress recordings, run Cypress with recording switched off
|
||||||
|
- name: run Cypress (without recording)
|
||||||
|
if: failure()
|
||||||
|
run: npx run-p --race start cypress:run
|
||||||
|
env:
|
||||||
|
CI: true
|
||||||
|
TERM: xterm-256color
|
||||||
|
NODE_ENV: production # prevent watching
|
||||||
|
CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}
|
||||||
|
DEBUG: commit-info,cypress:server:record
|
||||||
|
# https://docs.cypress.io/guides/guides/continuous-integration.html#Environment-variables
|
||||||
|
COMMIT_INFO_BRANCH: ${{ github.head_ref }}
|
||||||
|
COMMIT_INFO_AUTHOR: ${{ github.event.sender.login }}
|
||||||
|
COMMIT_INFO_SHA: ${{ github.event.after }}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
name: Release management
|
name: Release drafter
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
|
@ -9,6 +9,6 @@ jobs:
|
||||||
update-draft-release:
|
update-draft-release:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: toolmantim/release-drafter@v5.2.0
|
- uses: toolmantim/release-drafter@v5
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
@ -431,11 +431,11 @@ const example = new Choices(element, {
|
||||||
|
|
||||||
**Type:** `Boolean` **Default:** `true`
|
**Type:** `Boolean` **Default:** `true`
|
||||||
|
|
||||||
**Input types affected:** `text`, `select-multiple`
|
**Input types affected:** `text`
|
||||||
|
|
||||||
**Usage:** Whether the input should show a placeholder. Used in conjunction with `placeholderValue`. If `placeholder` is set to true and no value is passed to `placeholderValue`, the passed input's placeholder attribute will be used as the placeholder value.
|
**Usage:** Whether the input should show a placeholder. Used in conjunction with `placeholderValue`. If `placeholder` is set to true and no value is passed to `placeholderValue`, the passed input's placeholder attribute will be used as the placeholder value.
|
||||||
|
|
||||||
**Note:** For single select boxes, the recommended way of adding a placeholder is as follows:
|
**Note:** For select boxes, the recommended way of adding a placeholder is as follows:
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<select>
|
<select>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
describe('Choices - select multiple', () => {
|
describe('Choices - select multiple', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
cy.visit('/select-multiple.html');
|
cy.visit('/select-multiple');
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('scenarios', () => {
|
describe('scenarios', () => {
|
||||||
|
@ -486,20 +486,42 @@ describe('Choices - select multiple', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('placeholder', () => {
|
describe('placeholder via empty option value', () => {
|
||||||
/*
|
|
||||||
{
|
|
||||||
placeholder: true,
|
|
||||||
placeholderValue: 'I am a placeholder',
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
describe('when no value has been inputted', () => {
|
describe('when no value has been inputted', () => {
|
||||||
it('displays a placeholder', () => {
|
it('displays a placeholder', () => {
|
||||||
cy.get('[data-test-hook=placeholder]')
|
cy.get('[data-test-hook=placeholder-via-option-value]')
|
||||||
.find('.choices__input--cloned')
|
.find('.choices__input--cloned')
|
||||||
.should('have.attr', 'placeholder', 'I am a placeholder');
|
.should('have.attr', 'placeholder', 'I am a placeholder');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('when a value has been inputted', () => {
|
||||||
|
it('does not display a placeholder', () => {
|
||||||
|
cy.get('[data-test-hook=placeholder-via-option-value]')
|
||||||
|
.find('.choices__input--cloned')
|
||||||
|
.type('test')
|
||||||
|
.should('not.have.value', 'I am a placeholder');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('placeholder via option attribute', () => {
|
||||||
|
describe('when no value has been inputted', () => {
|
||||||
|
it('displays a placeholder', () => {
|
||||||
|
cy.get('[data-test-hook=placeholder-via-option-attr]')
|
||||||
|
.find('.choices__input--cloned')
|
||||||
|
.should('have.attr', 'placeholder', 'I am a placeholder');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when a value has been inputted', () => {
|
||||||
|
it('does not display a placeholder', () => {
|
||||||
|
cy.get('[data-test-hook=placeholder-via-option-attr]')
|
||||||
|
.find('.choices__input--cloned')
|
||||||
|
.type('test')
|
||||||
|
.should('not.have.value', 'I am a placeholder');
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('remote data', () => {
|
describe('remote data', () => {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
describe('Choices - select one', () => {
|
describe('Choices - select one', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
cy.visit('/select-one.html');
|
cy.visit('/select-one');
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('scenarios', () => {
|
describe('scenarios', () => {
|
||||||
|
@ -448,6 +448,102 @@ describe('Choices - select one', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('placeholder via empty option value', () => {
|
||||||
|
describe('when no choice has been selected', () => {
|
||||||
|
it('displays a placeholder', () => {
|
||||||
|
cy.get('[data-test-hook=placeholder-via-option-value]')
|
||||||
|
.find('.choices__list--single')
|
||||||
|
.children()
|
||||||
|
.first()
|
||||||
|
.should('have.class', 'choices__placeholder')
|
||||||
|
.and($placeholder => {
|
||||||
|
expect($placeholder).to.contain('I am a placeholder');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when a choice has been selected', () => {
|
||||||
|
it('does not display a placeholder', () => {
|
||||||
|
cy.get('[data-test-hook=placeholder-via-option-value]')
|
||||||
|
.find('.choices__input--cloned')
|
||||||
|
.focus();
|
||||||
|
|
||||||
|
cy.get('[data-test-hook=placeholder-via-option-value]')
|
||||||
|
.find('.choices__list--dropdown .choices__list')
|
||||||
|
.children()
|
||||||
|
.first()
|
||||||
|
.click();
|
||||||
|
|
||||||
|
cy.get('[data-test-hook=placeholder-via-option-value]')
|
||||||
|
.find('.choices__input--cloned')
|
||||||
|
.should('not.have.value', 'I am a placeholder');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when choice list is open', () => {
|
||||||
|
it('displays the placeholder choice first', () => {
|
||||||
|
cy.get('[data-test-hook=placeholder-via-option-value]')
|
||||||
|
.find('.choices__input--cloned')
|
||||||
|
.focus();
|
||||||
|
|
||||||
|
cy.get('[data-test-hook=placeholder-via-option-value]')
|
||||||
|
.find('.choices__list--dropdown .choices__list')
|
||||||
|
.children()
|
||||||
|
.first()
|
||||||
|
.should('have.class', 'choices__placeholder')
|
||||||
|
.should('have.text', 'I am a placeholder');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('placeholder via option attribute', () => {
|
||||||
|
describe('when no choice has been selected', () => {
|
||||||
|
it('displays a placeholder', () => {
|
||||||
|
cy.get('[data-test-hook=placeholder-via-option-attr]')
|
||||||
|
.find('.choices__list--single')
|
||||||
|
.children()
|
||||||
|
.first()
|
||||||
|
.should('have.class', 'choices__placeholder')
|
||||||
|
.and($placeholder => {
|
||||||
|
expect($placeholder).to.contain('I am a placeholder');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when a choice has been selected', () => {
|
||||||
|
it('does not display a placeholder', () => {
|
||||||
|
cy.get('[data-test-hook=placeholder-via-option-attr]')
|
||||||
|
.find('.choices__input--cloned')
|
||||||
|
.focus();
|
||||||
|
|
||||||
|
cy.get('[data-test-hook=placeholder-via-option-attr]')
|
||||||
|
.find('.choices__list--dropdown .choices__list')
|
||||||
|
.children()
|
||||||
|
.first()
|
||||||
|
.click();
|
||||||
|
|
||||||
|
cy.get('[data-test-hook=placeholder-via-option-attr]')
|
||||||
|
.find('.choices__input--cloned')
|
||||||
|
.should('not.have.value', 'I am a placeholder');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when choice list is open', () => {
|
||||||
|
it('displays the placeholder choice first', () => {
|
||||||
|
cy.get('[data-test-hook=placeholder-via-option-attr]')
|
||||||
|
.find('.choices__input--cloned')
|
||||||
|
.focus();
|
||||||
|
|
||||||
|
cy.get('[data-test-hook=placeholder-via-option-attr]')
|
||||||
|
.find('.choices__list--dropdown .choices__list')
|
||||||
|
.children()
|
||||||
|
.first()
|
||||||
|
.should('have.class', 'choices__placeholder')
|
||||||
|
.should('have.text', 'I am a placeholder');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('remote data', () => {
|
describe('remote data', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
cy.reload(true);
|
cy.reload(true);
|
||||||
|
@ -458,6 +554,7 @@ describe('Choices - select one', () => {
|
||||||
cy.get('[data-test-hook=remote-data]')
|
cy.get('[data-test-hook=remote-data]')
|
||||||
.find('.choices__list--single')
|
.find('.choices__list--single')
|
||||||
.children()
|
.children()
|
||||||
|
.should('have.length', 1)
|
||||||
.first()
|
.first()
|
||||||
.should('have.class', 'choices__placeholder')
|
.should('have.class', 'choices__placeholder')
|
||||||
.and($placeholder => {
|
.and($placeholder => {
|
||||||
|
@ -483,10 +580,14 @@ describe('Choices - select one', () => {
|
||||||
cy.get('[data-test-hook=remote-data]')
|
cy.get('[data-test-hook=remote-data]')
|
||||||
.find('.choices__list--dropdown .choices__list')
|
.find('.choices__list--dropdown .choices__list')
|
||||||
.children()
|
.children()
|
||||||
.should('have.length', 50)
|
.should('have.length', 51) // 50 choices + 1 placeholder choice
|
||||||
.each(($choice, index) => {
|
.each(($choice, index) => {
|
||||||
expect($choice.text().trim()).to.equal(`Label ${index + 1}`);
|
if (index === 0) {
|
||||||
expect($choice.data('value')).to.equal(`Value ${index + 1}`);
|
expect($choice.text().trim()).to.equal('I am a placeholder');
|
||||||
|
} else {
|
||||||
|
expect($choice.text().trim()).to.equal(`Label ${index}`);
|
||||||
|
expect($choice.data('value')).to.equal(`Value ${index}`);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
describe('Choices - text element', () => {
|
describe('Choices - text element', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
cy.visit('/text.html');
|
cy.visit('/text');
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('scenarios', () => {
|
describe('scenarios', () => {
|
||||||
|
|
|
@ -218,7 +218,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.choices__list--dropdown {
|
.choices__list--dropdown {
|
||||||
display: none;
|
visibility: hidden;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
@ -230,10 +230,11 @@
|
||||||
border-bottom-right-radius: 2.5px;
|
border-bottom-right-radius: 2.5px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
word-break: break-all;
|
word-break: break-all;
|
||||||
|
will-change: visibility;
|
||||||
}
|
}
|
||||||
|
|
||||||
.choices__list--dropdown.is-active {
|
.choices__list--dropdown.is-active {
|
||||||
display: block;
|
visibility: visible;
|
||||||
}
|
}
|
||||||
|
|
||||||
.is-open .choices__list--dropdown {
|
.is-open .choices__list--dropdown {
|
||||||
|
|
2
public/assets/styles/choices.min.css
vendored
2
public/assets/styles/choices.min.css
vendored
File diff suppressed because one or more lines are too long
|
@ -15,43 +15,46 @@
|
||||||
<link
|
<link
|
||||||
rel="apple-touch-icon"
|
rel="apple-touch-icon"
|
||||||
sizes="180x180"
|
sizes="180x180"
|
||||||
href="../assets/images/apple-touch-icon.png"
|
href="../../assets/images/apple-touch-icon.png"
|
||||||
/>
|
/>
|
||||||
<link
|
<link
|
||||||
rel="icon"
|
rel="icon"
|
||||||
type="image/png"
|
type="image/png"
|
||||||
href="../assets/images/favicon-32x32.png"
|
href="../../assets/images/favicon-32x32.png"
|
||||||
sizes="32x32"
|
sizes="32x32"
|
||||||
/>
|
/>
|
||||||
<link
|
<link
|
||||||
rel="icon"
|
rel="icon"
|
||||||
type="image/png"
|
type="image/png"
|
||||||
href="../assets/images/favicon-16x16.png"
|
href="../../assets/images/favicon-16x16.png"
|
||||||
sizes="16x16"
|
sizes="16x16"
|
||||||
/>
|
/>
|
||||||
<link rel="manifest" href="../assets/images/manifest.json" />
|
<link rel="manifest" href="../../assets/images/manifest.json" />
|
||||||
<link
|
<link
|
||||||
rel="mask-icon"
|
rel="mask-icon"
|
||||||
href="../assets/images/safari-pinned-tab.svg"
|
href="../../assets/images/safari-pinned-tab.svg"
|
||||||
color="#00bcd4"
|
color="#00bcd4"
|
||||||
/>
|
/>
|
||||||
<link rel="shortcut icon" href="../assets/images/favicon.ico" />
|
<link rel="shortcut icon" href="../../assets/images/favicon.ico" />
|
||||||
<meta
|
<meta
|
||||||
name="msapplication-config"
|
name="msapplication-config"
|
||||||
content="../assets/images/browserconfig.xml"
|
content="../../assets/images/browserconfig.xml"
|
||||||
/>
|
/>
|
||||||
<meta name="theme-color" content="#ffffff" />
|
<meta name="theme-color" content="#ffffff" />
|
||||||
|
|
||||||
<!-- Ignore these -->
|
<!-- Ignore these -->
|
||||||
<link rel="stylesheet" href="../assets/styles/base.min.css?version=6.0.3" />
|
<link
|
||||||
|
rel="stylesheet"
|
||||||
|
href="../../assets/styles/base.min.css?version=6.0.3"
|
||||||
|
/>
|
||||||
<!-- End ignore these -->
|
<!-- End ignore these -->
|
||||||
|
|
||||||
<!-- Choices includes -->
|
<!-- Choices includes -->
|
||||||
<link
|
<link
|
||||||
rel="stylesheet"
|
rel="stylesheet"
|
||||||
href="../assets/styles/choices.min.css?version=6.0.3"
|
href="../../assets/styles/choices.min.css?version=6.0.3"
|
||||||
/>
|
/>
|
||||||
<script src="../assets/scripts/choices.min.js?version=6.0.3"></script>
|
<script src="../../assets/scripts/choices.min.js?version=6.0.3"></script>
|
||||||
<!-- End Choices includes -->
|
<!-- End Choices includes -->
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
|
@ -194,14 +197,34 @@
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div data-test-hook="placeholder">
|
<div data-test-hook="placeholder-via-option-value">
|
||||||
<label for="choices-placeholder">Placeholder</label>
|
<label for="choices-placeholder-via-option-value"
|
||||||
|
>Placeholder via empty option value</label
|
||||||
|
>
|
||||||
<select
|
<select
|
||||||
class="form-control"
|
class="form-control"
|
||||||
name="choices-placeholder"
|
name="choices-placeholder-via-option-value"
|
||||||
id="choices-placeholder"
|
id="choices-placeholder-via-option-value"
|
||||||
multiple
|
multiple
|
||||||
>
|
>
|
||||||
|
<option value="">I am a placeholder</option>
|
||||||
|
<option value="Choice 1">Choice 1</option>
|
||||||
|
<option value="Choice 2">Choice 2</option>
|
||||||
|
<option value="Choice 3">Choice 3</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div data-test-hook="placeholder-via-option-attr">
|
||||||
|
<label for="choices-placeholder-via-option-attr"
|
||||||
|
>Placeholder via option attribute</label
|
||||||
|
>
|
||||||
|
<select
|
||||||
|
class="form-control"
|
||||||
|
name="choices-placeholder-via-option-attr"
|
||||||
|
id="choices-placeholder-via-option-attr"
|
||||||
|
multiple
|
||||||
|
>
|
||||||
|
<option placeholder>I am a placeholder</option>
|
||||||
<option value="Choice 1">Choice 1</option>
|
<option value="Choice 1">Choice 1</option>
|
||||||
<option value="Choice 2">Choice 2</option>
|
<option value="Choice 2">Choice 2</option>
|
||||||
<option value="Choice 3">Choice 3</option>
|
<option value="Choice 3">Choice 3</option>
|
||||||
|
@ -215,7 +238,9 @@
|
||||||
name="choices-remote-data"
|
name="choices-remote-data"
|
||||||
id="choices-remote-data"
|
id="choices-remote-data"
|
||||||
multiple
|
multiple
|
||||||
></select>
|
>
|
||||||
|
<option value="">I am a placeholder</option>
|
||||||
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div data-test-hook="scrolling-dropdown">
|
<div data-test-hook="scrolling-dropdown">
|
||||||
|
@ -373,10 +398,9 @@
|
||||||
searchFloor: 5,
|
searchFloor: 5,
|
||||||
});
|
});
|
||||||
|
|
||||||
new Choices('#choices-placeholder', {
|
new Choices('#choices-placeholder-via-option-value');
|
||||||
placeholder: true,
|
|
||||||
placeholderValue: 'I am a placeholder',
|
new Choices('#choices-placeholder-via-option-attr');
|
||||||
});
|
|
||||||
|
|
||||||
new Choices('#choices-remote-data', {
|
new Choices('#choices-remote-data', {
|
||||||
shouldSort: false,
|
shouldSort: false,
|
|
@ -15,43 +15,46 @@
|
||||||
<link
|
<link
|
||||||
rel="apple-touch-icon"
|
rel="apple-touch-icon"
|
||||||
sizes="180x180"
|
sizes="180x180"
|
||||||
href="../assets/images/apple-touch-icon.png"
|
href="../../assets/images/apple-touch-icon.png"
|
||||||
/>
|
/>
|
||||||
<link
|
<link
|
||||||
rel="icon"
|
rel="icon"
|
||||||
type="image/png"
|
type="image/png"
|
||||||
href="../assets/images/favicon-32x32.png"
|
href="../../assets/images/favicon-32x32.png"
|
||||||
sizes="32x32"
|
sizes="32x32"
|
||||||
/>
|
/>
|
||||||
<link
|
<link
|
||||||
rel="icon"
|
rel="icon"
|
||||||
type="image/png"
|
type="image/png"
|
||||||
href="../assets/images/favicon-16x16.png"
|
href="../../assets/images/favicon-16x16.png"
|
||||||
sizes="16x16"
|
sizes="16x16"
|
||||||
/>
|
/>
|
||||||
<link rel="manifest" href="../assets/images/manifest.json" />
|
<link rel="manifest" href="../../assets/images/manifest.json" />
|
||||||
<link
|
<link
|
||||||
rel="mask-icon"
|
rel="mask-icon"
|
||||||
href="../assets/images/safari-pinned-tab.svg"
|
href="../../assets/images/safari-pinned-tab.svg"
|
||||||
color="#00bcd4"
|
color="#00bcd4"
|
||||||
/>
|
/>
|
||||||
<link rel="shortcut icon" href="../assets/images/favicon.ico" />
|
<link rel="shortcut icon" href="../../assets/images/favicon.ico" />
|
||||||
<meta
|
<meta
|
||||||
name="msapplication-config"
|
name="msapplication-config"
|
||||||
content="../assets/images/browserconfig.xml"
|
content="../../assets/images/browserconfig.xml"
|
||||||
/>
|
/>
|
||||||
<meta name="theme-color" content="#ffffff" />
|
<meta name="theme-color" content="#ffffff" />
|
||||||
|
|
||||||
<!-- Ignore these -->
|
<!-- Ignore these -->
|
||||||
<link rel="stylesheet" href="../assets/styles/base.min.css?version=6.0.3" />
|
<link
|
||||||
|
rel="stylesheet"
|
||||||
|
href="../../assets/styles/base.min.css?version=6.0.3"
|
||||||
|
/>
|
||||||
<!-- End ignore these -->
|
<!-- End ignore these -->
|
||||||
|
|
||||||
<!-- Choices includes -->
|
<!-- Choices includes -->
|
||||||
<link
|
<link
|
||||||
rel="stylesheet"
|
rel="stylesheet"
|
||||||
href="../assets/styles/choices.min.css?version=6.0.3"
|
href="../../assets/styles/choices.min.css?version=6.0.3"
|
||||||
/>
|
/>
|
||||||
<script src="../assets/scripts/choices.min.js?version=6.0.3"></script>
|
<script src="../../assets/scripts/choices.min.js?version=6.0.3"></script>
|
||||||
<!-- End Choices includes -->
|
<!-- End Choices includes -->
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
|
@ -178,13 +181,47 @@
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div data-test-hook="placeholder-via-option-value">
|
||||||
|
<label for="choices-placeholder-via-option-value"
|
||||||
|
>Placeholder via empty option value</label
|
||||||
|
>
|
||||||
|
<select
|
||||||
|
class="form-control"
|
||||||
|
name="choices-placeholder-via-option-value"
|
||||||
|
id="choices-placeholder-via-option-value"
|
||||||
|
>
|
||||||
|
<option value="">I am a placeholder</option>
|
||||||
|
<option value="Choice 1">Choice 1</option>
|
||||||
|
<option value="Choice 2">Choice 2</option>
|
||||||
|
<option value="Choice 3">Choice 3</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div data-test-hook="placeholder-via-option-attr">
|
||||||
|
<label for="choices-placeholder-via-option-attr"
|
||||||
|
>Placeholder via option attribute</label
|
||||||
|
>
|
||||||
|
<select
|
||||||
|
class="form-control"
|
||||||
|
name="choices-placeholder-via-option-attr"
|
||||||
|
id="choices-placeholder-via-option-attr"
|
||||||
|
>
|
||||||
|
<option placeholder>I am a placeholder</option>
|
||||||
|
<option value="Choice 1">Choice 1</option>
|
||||||
|
<option value="Choice 2">Choice 2</option>
|
||||||
|
<option value="Choice 3">Choice 3</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div data-test-hook="remote-data">
|
<div data-test-hook="remote-data">
|
||||||
<label for="choices-remote-data">Remote data</label>
|
<label for="choices-remote-data">Remote data</label>
|
||||||
<select
|
<select
|
||||||
class="form-control"
|
class="form-control"
|
||||||
name="choices-remote-data"
|
name="choices-remote-data"
|
||||||
id="choices-remote-data"
|
id="choices-remote-data"
|
||||||
></select>
|
>
|
||||||
|
<option value="">I am a placeholder</option>
|
||||||
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div data-test-hook="scrolling-dropdown">
|
<div data-test-hook="scrolling-dropdown">
|
||||||
|
@ -359,6 +396,10 @@
|
||||||
searchFloor: 5,
|
searchFloor: 5,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
new Choices('#choices-placeholder-via-option-value');
|
||||||
|
|
||||||
|
new Choices('#choices-placeholder-via-option-attr');
|
||||||
|
|
||||||
new Choices('#choices-remote-data', {
|
new Choices('#choices-remote-data', {
|
||||||
shouldSort: false,
|
shouldSort: false,
|
||||||
}).setChoices(async () => {
|
}).setChoices(async () => {
|
|
@ -15,43 +15,46 @@
|
||||||
<link
|
<link
|
||||||
rel="apple-touch-icon"
|
rel="apple-touch-icon"
|
||||||
sizes="180x180"
|
sizes="180x180"
|
||||||
href="../assets/images/apple-touch-icon.png"
|
href="../../assets/images/apple-touch-icon.png"
|
||||||
/>
|
/>
|
||||||
<link
|
<link
|
||||||
rel="icon"
|
rel="icon"
|
||||||
type="image/png"
|
type="image/png"
|
||||||
href="../assets/images/favicon-32x32.png"
|
href="../../assets/images/favicon-32x32.png"
|
||||||
sizes="32x32"
|
sizes="32x32"
|
||||||
/>
|
/>
|
||||||
<link
|
<link
|
||||||
rel="icon"
|
rel="icon"
|
||||||
type="image/png"
|
type="image/png"
|
||||||
href="../assets/images/favicon-16x16.png"
|
href="../../assets/images/favicon-16x16.png"
|
||||||
sizes="16x16"
|
sizes="16x16"
|
||||||
/>
|
/>
|
||||||
<link rel="manifest" href="../assets/images/manifest.json" />
|
<link rel="manifest" href="../../assets/images/manifest.json" />
|
||||||
<link
|
<link
|
||||||
rel="mask-icon"
|
rel="mask-icon"
|
||||||
href="../assets/images/safari-pinned-tab.svg"
|
href="../../assets/images/safari-pinned-tab.svg"
|
||||||
color="#00bcd4"
|
color="#00bcd4"
|
||||||
/>
|
/>
|
||||||
<link rel="shortcut icon" href="../assets/images/favicon.ico" />
|
<link rel="shortcut icon" href="../../assets/images/favicon.ico" />
|
||||||
<meta
|
<meta
|
||||||
name="msapplication-config"
|
name="msapplication-config"
|
||||||
content="../assets/images/browserconfig.xml"
|
content="../../assets/images/browserconfig.xml"
|
||||||
/>
|
/>
|
||||||
<meta name="theme-color" content="#ffffff" />
|
<meta name="theme-color" content="#ffffff" />
|
||||||
|
|
||||||
<!-- Ignore these -->
|
<!-- Ignore these -->
|
||||||
<link rel="stylesheet" href="../assets/styles/base.min.css?version=6.0.3" />
|
<link
|
||||||
|
rel="stylesheet"
|
||||||
|
href="../../assets/styles/base.min.css?version=6.0.3"
|
||||||
|
/>
|
||||||
<!-- End ignore these -->
|
<!-- End ignore these -->
|
||||||
|
|
||||||
<!-- Choices includes -->
|
<!-- Choices includes -->
|
||||||
<link
|
<link
|
||||||
rel="stylesheet"
|
rel="stylesheet"
|
||||||
href="../assets/styles/choices.min.css?version=6.0.3"
|
href="../../assets/styles/choices.min.css?version=6.0.3"
|
||||||
/>
|
/>
|
||||||
<script src="../assets/scripts/choices.min.js?version=6.0.3"></script>
|
<script src="../../assets/scripts/choices.min.js?version=6.0.3"></script>
|
||||||
<!-- End Choices includes -->
|
<!-- End Choices includes -->
|
||||||
</head>
|
</head>
|
||||||
|
|
|
@ -149,6 +149,7 @@ class Choices {
|
||||||
* @type {HTMLElement['dir']}
|
* @type {HTMLElement['dir']}
|
||||||
*/
|
*/
|
||||||
this._direction = this.passedElement.element.dir;
|
this._direction = this.passedElement.element.dir;
|
||||||
|
|
||||||
if (!this._direction) {
|
if (!this._direction) {
|
||||||
const { direction: elementDirection } = window.getComputedStyle(
|
const { direction: elementDirection } = window.getComputedStyle(
|
||||||
this.passedElement.element,
|
this.passedElement.element,
|
||||||
|
@ -160,6 +161,7 @@ class Choices {
|
||||||
this._direction = elementDirection;
|
this._direction = elementDirection;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this._idNames = {
|
this._idNames = {
|
||||||
itemChoice: 'item-choice',
|
itemChoice: 'item-choice',
|
||||||
};
|
};
|
||||||
|
@ -849,7 +851,9 @@ class Choices {
|
||||||
let choiceLimit = rendererableChoices.length;
|
let choiceLimit = rendererableChoices.length;
|
||||||
|
|
||||||
// Prepend placeholeder
|
// Prepend placeholeder
|
||||||
const sortedChoices = [...placeholderChoices, ...normalChoices];
|
const sortedChoices = this._isSelectOneElement
|
||||||
|
? [...placeholderChoices, ...normalChoices]
|
||||||
|
: normalChoices;
|
||||||
|
|
||||||
if (this._isSearching) {
|
if (this._isSearching) {
|
||||||
choiceLimit = searchResultLimit;
|
choiceLimit = searchResultLimit;
|
||||||
|
@ -2075,7 +2079,7 @@ class Choices {
|
||||||
label: o.innerHTML,
|
label: o.innerHTML,
|
||||||
selected: o.selected,
|
selected: o.selected,
|
||||||
disabled: o.disabled || o.parentNode.disabled,
|
disabled: o.disabled || o.parentNode.disabled,
|
||||||
placeholder: o.hasAttribute('placeholder'),
|
placeholder: o.value === '' || o.hasAttribute('placeholder'),
|
||||||
customProperties: o.getAttribute('data-custom-properties'),
|
customProperties: o.getAttribute('data-custom-properties'),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -2224,14 +2228,28 @@ class Choices {
|
||||||
}
|
}
|
||||||
|
|
||||||
_generatePlaceholderValue() {
|
_generatePlaceholderValue() {
|
||||||
if (this._isSelectOneElement) {
|
if (this._isSelectElement) {
|
||||||
return false;
|
const { placeholderOption } = this.passedElement;
|
||||||
|
|
||||||
|
return placeholderOption ? placeholderOption.text : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.config.placeholder
|
const { placeholder, placeholderValue } = this.config;
|
||||||
? this.config.placeholderValue ||
|
const {
|
||||||
this.passedElement.element.getAttribute('placeholder')
|
element: { dataset },
|
||||||
: false;
|
} = this.passedElement;
|
||||||
|
|
||||||
|
if (placeholder) {
|
||||||
|
if (placeholderValue) {
|
||||||
|
return placeholderValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dataset.placeholder) {
|
||||||
|
return dataset.placeholder;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ===== End of Private functions ====== */
|
/* ===== End of Private functions ====== */
|
||||||
|
|
|
@ -1873,5 +1873,96 @@ describe('choices', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('_generatePlaceholderValue', () => {
|
||||||
|
describe('select element', () => {
|
||||||
|
describe('when a placeholder option is defined', () => {
|
||||||
|
it('returns the text value of the placeholder option', () => {
|
||||||
|
const placeholderValue = 'I am a placeholder';
|
||||||
|
|
||||||
|
instance._isSelectElement = true;
|
||||||
|
instance.passedElement.placeholderOption = {
|
||||||
|
text: placeholderValue,
|
||||||
|
};
|
||||||
|
|
||||||
|
const value = instance._generatePlaceholderValue();
|
||||||
|
expect(value).to.equal(placeholderValue);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when a placeholder option is not defined', () => {
|
||||||
|
it('returns false', () => {
|
||||||
|
instance._isSelectElement = true;
|
||||||
|
instance.passedElement.placeholderOption = undefined;
|
||||||
|
|
||||||
|
const value = instance._generatePlaceholderValue();
|
||||||
|
expect(value).to.equal(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('text input', () => {
|
||||||
|
describe('when the placeholder config option is set to true', () => {
|
||||||
|
describe('when the placeholderValue config option is defined', () => {
|
||||||
|
it('returns placeholderValue', () => {
|
||||||
|
const placeholderValue = 'I am a placeholder';
|
||||||
|
|
||||||
|
instance._isSelectElement = false;
|
||||||
|
instance.config.placeholder = true;
|
||||||
|
instance.config.placeholderValue = placeholderValue;
|
||||||
|
|
||||||
|
const value = instance._generatePlaceholderValue();
|
||||||
|
expect(value).to.equal(placeholderValue);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when the placeholderValue config option is not defined', () => {
|
||||||
|
describe('when the placeholder attribute is defined on the passed element', () => {
|
||||||
|
it('returns the value of the placeholder attribute', () => {
|
||||||
|
const placeholderValue = 'I am a placeholder';
|
||||||
|
|
||||||
|
instance._isSelectElement = false;
|
||||||
|
instance.config.placeholder = true;
|
||||||
|
instance.config.placeholderValue = undefined;
|
||||||
|
instance.passedElement.element = {
|
||||||
|
dataset: {
|
||||||
|
placeholder: placeholderValue,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const value = instance._generatePlaceholderValue();
|
||||||
|
expect(value).to.equal(placeholderValue);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when the placeholder attribute is not defined on the passed element', () => {
|
||||||
|
it('returns false', () => {
|
||||||
|
instance._isSelectElement = false;
|
||||||
|
instance.config.placeholder = true;
|
||||||
|
instance.config.placeholderValue = undefined;
|
||||||
|
instance.passedElement.element = {
|
||||||
|
dataset: {
|
||||||
|
placeholder: undefined,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const value = instance._generatePlaceholderValue();
|
||||||
|
expect(value).to.equal(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when the placeholder config option is set to false', () => {
|
||||||
|
it('returns false', () => {
|
||||||
|
instance._isSelectElement = false;
|
||||||
|
instance.config.placeholder = false;
|
||||||
|
|
||||||
|
const value = instance._generatePlaceholderValue();
|
||||||
|
expect(value).to.equal(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -39,16 +39,19 @@ export const TEMPLATES = /** @type {Templates} */ ({
|
||||||
|
|
||||||
return div;
|
return div;
|
||||||
},
|
},
|
||||||
|
|
||||||
containerInner({ containerInner }) {
|
containerInner({ containerInner }) {
|
||||||
return Object.assign(document.createElement('div'), {
|
return Object.assign(document.createElement('div'), {
|
||||||
className: containerInner,
|
className: containerInner,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
itemList({ list, listSingle, listItems }, isSelectOneElement) {
|
itemList({ list, listSingle, listItems }, isSelectOneElement) {
|
||||||
return Object.assign(document.createElement('div'), {
|
return Object.assign(document.createElement('div'), {
|
||||||
className: `${list} ${isSelectOneElement ? listSingle : listItems}`,
|
className: `${list} ${isSelectOneElement ? listSingle : listItems}`,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
placeholder({ placeholder }, value) {
|
placeholder({ placeholder }, value) {
|
||||||
return Object.assign(document.createElement('div'), {
|
return Object.assign(document.createElement('div'), {
|
||||||
className: placeholder,
|
className: placeholder,
|
||||||
|
@ -93,6 +96,7 @@ export const TEMPLATES = /** @type {Templates} */ ({
|
||||||
if (isPlaceholder) {
|
if (isPlaceholder) {
|
||||||
div.classList.add(placeholder);
|
div.classList.add(placeholder);
|
||||||
}
|
}
|
||||||
|
|
||||||
div.classList.add(highlighted ? highlightedState : itemSelectable);
|
div.classList.add(highlighted ? highlightedState : itemSelectable);
|
||||||
|
|
||||||
if (removeItemButton) {
|
if (removeItemButton) {
|
||||||
|
@ -117,6 +121,7 @@ export const TEMPLATES = /** @type {Templates} */ ({
|
||||||
|
|
||||||
return div;
|
return div;
|
||||||
},
|
},
|
||||||
|
|
||||||
choiceList({ list }, isSelectOneElement) {
|
choiceList({ list }, isSelectOneElement) {
|
||||||
const div = Object.assign(document.createElement('div'), {
|
const div = Object.assign(document.createElement('div'), {
|
||||||
className: list,
|
className: list,
|
||||||
|
@ -196,6 +201,7 @@ export const TEMPLATES = /** @type {Templates} */ ({
|
||||||
|
|
||||||
return div;
|
return div;
|
||||||
},
|
},
|
||||||
|
|
||||||
input({ input, inputCloned }, placeholderValue) {
|
input({ input, inputCloned }, placeholderValue) {
|
||||||
const inp = Object.assign(document.createElement('input'), {
|
const inp = Object.assign(document.createElement('input'), {
|
||||||
type: 'text',
|
type: 'text',
|
||||||
|
@ -211,6 +217,7 @@ export const TEMPLATES = /** @type {Templates} */ ({
|
||||||
|
|
||||||
return inp;
|
return inp;
|
||||||
},
|
},
|
||||||
|
|
||||||
dropdown({ list, listDropdown }) {
|
dropdown({ list, listDropdown }) {
|
||||||
const div = document.createElement('div');
|
const div = document.createElement('div');
|
||||||
|
|
||||||
|
@ -219,6 +226,7 @@ export const TEMPLATES = /** @type {Templates} */ ({
|
||||||
|
|
||||||
return div;
|
return div;
|
||||||
},
|
},
|
||||||
|
|
||||||
notice({ item, itemChoice, noResults, noChoices }, innerHTML, type = '') {
|
notice({ item, itemChoice, noResults, noChoices }, innerHTML, type = '') {
|
||||||
const classes = [item, itemChoice];
|
const classes = [item, itemChoice];
|
||||||
|
|
||||||
|
@ -233,6 +241,7 @@ export const TEMPLATES = /** @type {Templates} */ ({
|
||||||
className: classes.join(' '),
|
className: classes.join(' '),
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
option({ label, value, customProperties, active, disabled }) {
|
option({ label, value, customProperties, active, disabled }) {
|
||||||
const opt = new Option(label, value, false, active);
|
const opt = new Option(label, value, false, active);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue