* 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 |
|
@ -1,6 +1,9 @@
|
|||
name-template: 'Draft (next release)'
|
||||
tag-template: 'v$NEXT_PATCH_VERSION'
|
||||
sort-direction: descending
|
||||
exclude-labels:
|
||||
- 'skip-changelog'
|
||||
- 'release'
|
||||
categories:
|
||||
- title: '🚨 Breaking changes'
|
||||
labels:
|
||||
|
|
|
@ -29,7 +29,7 @@ jobs:
|
|||
env:
|
||||
HUSKY_SKIP_INSTALL: true
|
||||
|
||||
- name: run Cypress CI
|
||||
- name: run Cypress (with recording)
|
||||
run: npx run-p --race start cypress:ci
|
||||
env:
|
||||
CI: true
|
||||
|
@ -41,3 +41,18 @@ jobs:
|
|||
COMMIT_INFO_BRANCH: ${{ github.head_ref }}
|
||||
COMMIT_INFO_AUTHOR: ${{ github.event.sender.login }}
|
||||
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:
|
||||
push:
|
||||
|
@ -9,6 +9,6 @@ jobs:
|
|||
update-draft-release:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: toolmantim/release-drafter@v5.2.0
|
||||
- uses: toolmantim/release-drafter@v5
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
@ -431,11 +431,11 @@ const example = new Choices(element, {
|
|||
|
||||
**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.
|
||||
|
||||
**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
|
||||
<select>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
describe('Choices - select multiple', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('/select-multiple.html');
|
||||
cy.visit('/select-multiple');
|
||||
});
|
||||
|
||||
describe('scenarios', () => {
|
||||
|
@ -486,20 +486,42 @@ describe('Choices - select multiple', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('placeholder', () => {
|
||||
/*
|
||||
{
|
||||
placeholder: true,
|
||||
placeholderValue: 'I am a placeholder',
|
||||
}
|
||||
*/
|
||||
describe('placeholder via empty option value', () => {
|
||||
describe('when no value has been inputted', () => {
|
||||
it('displays a placeholder', () => {
|
||||
cy.get('[data-test-hook=placeholder]')
|
||||
cy.get('[data-test-hook=placeholder-via-option-value]')
|
||||
.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-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', () => {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
describe('Choices - select one', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('/select-one.html');
|
||||
cy.visit('/select-one');
|
||||
});
|
||||
|
||||
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', () => {
|
||||
beforeEach(() => {
|
||||
cy.reload(true);
|
||||
|
@ -458,6 +554,7 @@ describe('Choices - select one', () => {
|
|||
cy.get('[data-test-hook=remote-data]')
|
||||
.find('.choices__list--single')
|
||||
.children()
|
||||
.should('have.length', 1)
|
||||
.first()
|
||||
.should('have.class', 'choices__placeholder')
|
||||
.and($placeholder => {
|
||||
|
@ -483,10 +580,14 @@ describe('Choices - select one', () => {
|
|||
cy.get('[data-test-hook=remote-data]')
|
||||
.find('.choices__list--dropdown .choices__list')
|
||||
.children()
|
||||
.should('have.length', 50)
|
||||
.should('have.length', 51) // 50 choices + 1 placeholder choice
|
||||
.each(($choice, index) => {
|
||||
expect($choice.text().trim()).to.equal(`Label ${index + 1}`);
|
||||
expect($choice.data('value')).to.equal(`Value ${index + 1}`);
|
||||
if (index === 0) {
|
||||
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', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('/text.html');
|
||||
cy.visit('/text');
|
||||
});
|
||||
|
||||
describe('scenarios', () => {
|
||||
|
|
|
@ -218,7 +218,7 @@
|
|||
}
|
||||
|
||||
.choices__list--dropdown {
|
||||
display: none;
|
||||
visibility: hidden;
|
||||
z-index: 1;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
|
@ -230,10 +230,11 @@
|
|||
border-bottom-right-radius: 2.5px;
|
||||
overflow: hidden;
|
||||
word-break: break-all;
|
||||
will-change: visibility;
|
||||
}
|
||||
|
||||
.choices__list--dropdown.is-active {
|
||||
display: block;
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.is-open .choices__list--dropdown {
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -15,43 +15,46 @@
|
|||
<link
|
||||
rel="apple-touch-icon"
|
||||
sizes="180x180"
|
||||
href="../assets/images/apple-touch-icon.png"
|
||||
href="../../assets/images/apple-touch-icon.png"
|
||||
/>
|
||||
<link
|
||||
rel="icon"
|
||||
type="image/png"
|
||||
href="../assets/images/favicon-32x32.png"
|
||||
href="../../assets/images/favicon-32x32.png"
|
||||
sizes="32x32"
|
||||
/>
|
||||
<link
|
||||
rel="icon"
|
||||
type="image/png"
|
||||
href="../assets/images/favicon-16x16.png"
|
||||
href="../../assets/images/favicon-16x16.png"
|
||||
sizes="16x16"
|
||||
/>
|
||||
<link rel="manifest" href="../assets/images/manifest.json" />
|
||||
<link rel="manifest" href="../../assets/images/manifest.json" />
|
||||
<link
|
||||
rel="mask-icon"
|
||||
href="../assets/images/safari-pinned-tab.svg"
|
||||
href="../../assets/images/safari-pinned-tab.svg"
|
||||
color="#00bcd4"
|
||||
/>
|
||||
<link rel="shortcut icon" href="../assets/images/favicon.ico" />
|
||||
<link rel="shortcut icon" href="../../assets/images/favicon.ico" />
|
||||
<meta
|
||||
name="msapplication-config"
|
||||
content="../assets/images/browserconfig.xml"
|
||||
content="../../assets/images/browserconfig.xml"
|
||||
/>
|
||||
<meta name="theme-color" content="#ffffff" />
|
||||
|
||||
<!-- 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 -->
|
||||
|
||||
<!-- Choices includes -->
|
||||
<link
|
||||
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 -->
|
||||
</head>
|
||||
|
||||
|
@ -194,14 +197,34 @@
|
|||
</select>
|
||||
</div>
|
||||
|
||||
<div data-test-hook="placeholder">
|
||||
<label for="choices-placeholder">Placeholder</label>
|
||||
<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"
|
||||
id="choices-placeholder"
|
||||
name="choices-placeholder-via-option-value"
|
||||
id="choices-placeholder-via-option-value"
|
||||
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 2">Choice 2</option>
|
||||
<option value="Choice 3">Choice 3</option>
|
||||
|
@ -215,7 +238,9 @@
|
|||
name="choices-remote-data"
|
||||
id="choices-remote-data"
|
||||
multiple
|
||||
></select>
|
||||
>
|
||||
<option value="">I am a placeholder</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div data-test-hook="scrolling-dropdown">
|
||||
|
@ -373,10 +398,9 @@
|
|||
searchFloor: 5,
|
||||
});
|
||||
|
||||
new Choices('#choices-placeholder', {
|
||||
placeholder: true,
|
||||
placeholderValue: 'I am a placeholder',
|
||||
});
|
||||
new Choices('#choices-placeholder-via-option-value');
|
||||
|
||||
new Choices('#choices-placeholder-via-option-attr');
|
||||
|
||||
new Choices('#choices-remote-data', {
|
||||
shouldSort: false,
|
|
@ -15,43 +15,46 @@
|
|||
<link
|
||||
rel="apple-touch-icon"
|
||||
sizes="180x180"
|
||||
href="../assets/images/apple-touch-icon.png"
|
||||
href="../../assets/images/apple-touch-icon.png"
|
||||
/>
|
||||
<link
|
||||
rel="icon"
|
||||
type="image/png"
|
||||
href="../assets/images/favicon-32x32.png"
|
||||
href="../../assets/images/favicon-32x32.png"
|
||||
sizes="32x32"
|
||||
/>
|
||||
<link
|
||||
rel="icon"
|
||||
type="image/png"
|
||||
href="../assets/images/favicon-16x16.png"
|
||||
href="../../assets/images/favicon-16x16.png"
|
||||
sizes="16x16"
|
||||
/>
|
||||
<link rel="manifest" href="../assets/images/manifest.json" />
|
||||
<link rel="manifest" href="../../assets/images/manifest.json" />
|
||||
<link
|
||||
rel="mask-icon"
|
||||
href="../assets/images/safari-pinned-tab.svg"
|
||||
href="../../assets/images/safari-pinned-tab.svg"
|
||||
color="#00bcd4"
|
||||
/>
|
||||
<link rel="shortcut icon" href="../assets/images/favicon.ico" />
|
||||
<link rel="shortcut icon" href="../../assets/images/favicon.ico" />
|
||||
<meta
|
||||
name="msapplication-config"
|
||||
content="../assets/images/browserconfig.xml"
|
||||
content="../../assets/images/browserconfig.xml"
|
||||
/>
|
||||
<meta name="theme-color" content="#ffffff" />
|
||||
|
||||
<!-- 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 -->
|
||||
|
||||
<!-- Choices includes -->
|
||||
<link
|
||||
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 -->
|
||||
</head>
|
||||
|
||||
|
@ -178,13 +181,47 @@
|
|||
</select>
|
||||
</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">
|
||||
<label for="choices-remote-data">Remote data</label>
|
||||
<select
|
||||
class="form-control"
|
||||
name="choices-remote-data"
|
||||
id="choices-remote-data"
|
||||
></select>
|
||||
>
|
||||
<option value="">I am a placeholder</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div data-test-hook="scrolling-dropdown">
|
||||
|
@ -359,6 +396,10 @@
|
|||
searchFloor: 5,
|
||||
});
|
||||
|
||||
new Choices('#choices-placeholder-via-option-value');
|
||||
|
||||
new Choices('#choices-placeholder-via-option-attr');
|
||||
|
||||
new Choices('#choices-remote-data', {
|
||||
shouldSort: false,
|
||||
}).setChoices(async () => {
|
|
@ -15,43 +15,46 @@
|
|||
<link
|
||||
rel="apple-touch-icon"
|
||||
sizes="180x180"
|
||||
href="../assets/images/apple-touch-icon.png"
|
||||
href="../../assets/images/apple-touch-icon.png"
|
||||
/>
|
||||
<link
|
||||
rel="icon"
|
||||
type="image/png"
|
||||
href="../assets/images/favicon-32x32.png"
|
||||
href="../../assets/images/favicon-32x32.png"
|
||||
sizes="32x32"
|
||||
/>
|
||||
<link
|
||||
rel="icon"
|
||||
type="image/png"
|
||||
href="../assets/images/favicon-16x16.png"
|
||||
href="../../assets/images/favicon-16x16.png"
|
||||
sizes="16x16"
|
||||
/>
|
||||
<link rel="manifest" href="../assets/images/manifest.json" />
|
||||
<link rel="manifest" href="../../assets/images/manifest.json" />
|
||||
<link
|
||||
rel="mask-icon"
|
||||
href="../assets/images/safari-pinned-tab.svg"
|
||||
href="../../assets/images/safari-pinned-tab.svg"
|
||||
color="#00bcd4"
|
||||
/>
|
||||
<link rel="shortcut icon" href="../assets/images/favicon.ico" />
|
||||
<link rel="shortcut icon" href="../../assets/images/favicon.ico" />
|
||||
<meta
|
||||
name="msapplication-config"
|
||||
content="../assets/images/browserconfig.xml"
|
||||
content="../../assets/images/browserconfig.xml"
|
||||
/>
|
||||
<meta name="theme-color" content="#ffffff" />
|
||||
|
||||
<!-- 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 -->
|
||||
|
||||
<!-- Choices includes -->
|
||||
<link
|
||||
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 -->
|
||||
</head>
|
||||
|
|
@ -149,6 +149,7 @@ class Choices {
|
|||
* @type {HTMLElement['dir']}
|
||||
*/
|
||||
this._direction = this.passedElement.element.dir;
|
||||
|
||||
if (!this._direction) {
|
||||
const { direction: elementDirection } = window.getComputedStyle(
|
||||
this.passedElement.element,
|
||||
|
@ -160,6 +161,7 @@ class Choices {
|
|||
this._direction = elementDirection;
|
||||
}
|
||||
}
|
||||
|
||||
this._idNames = {
|
||||
itemChoice: 'item-choice',
|
||||
};
|
||||
|
@ -849,7 +851,9 @@ class Choices {
|
|||
let choiceLimit = rendererableChoices.length;
|
||||
|
||||
// Prepend placeholeder
|
||||
const sortedChoices = [...placeholderChoices, ...normalChoices];
|
||||
const sortedChoices = this._isSelectOneElement
|
||||
? [...placeholderChoices, ...normalChoices]
|
||||
: normalChoices;
|
||||
|
||||
if (this._isSearching) {
|
||||
choiceLimit = searchResultLimit;
|
||||
|
@ -2075,7 +2079,7 @@ class Choices {
|
|||
label: o.innerHTML,
|
||||
selected: o.selected,
|
||||
disabled: o.disabled || o.parentNode.disabled,
|
||||
placeholder: o.hasAttribute('placeholder'),
|
||||
placeholder: o.value === '' || o.hasAttribute('placeholder'),
|
||||
customProperties: o.getAttribute('data-custom-properties'),
|
||||
});
|
||||
});
|
||||
|
@ -2224,14 +2228,28 @@ class Choices {
|
|||
}
|
||||
|
||||
_generatePlaceholderValue() {
|
||||
if (this._isSelectOneElement) {
|
||||
return false;
|
||||
if (this._isSelectElement) {
|
||||
const { placeholderOption } = this.passedElement;
|
||||
|
||||
return placeholderOption ? placeholderOption.text : false;
|
||||
}
|
||||
|
||||
return this.config.placeholder
|
||||
? this.config.placeholderValue ||
|
||||
this.passedElement.element.getAttribute('placeholder')
|
||||
: false;
|
||||
const { placeholder, placeholderValue } = this.config;
|
||||
const {
|
||||
element: { dataset },
|
||||
} = this.passedElement;
|
||||
|
||||
if (placeholder) {
|
||||
if (placeholderValue) {
|
||||
return placeholderValue;
|
||||
}
|
||||
|
||||
if (dataset.placeholder) {
|
||||
return dataset.placeholder;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* ===== 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;
|
||||
},
|
||||
|
||||
containerInner({ containerInner }) {
|
||||
return Object.assign(document.createElement('div'), {
|
||||
className: containerInner,
|
||||
});
|
||||
},
|
||||
|
||||
itemList({ list, listSingle, listItems }, isSelectOneElement) {
|
||||
return Object.assign(document.createElement('div'), {
|
||||
className: `${list} ${isSelectOneElement ? listSingle : listItems}`,
|
||||
});
|
||||
},
|
||||
|
||||
placeholder({ placeholder }, value) {
|
||||
return Object.assign(document.createElement('div'), {
|
||||
className: placeholder,
|
||||
|
@ -93,6 +96,7 @@ export const TEMPLATES = /** @type {Templates} */ ({
|
|||
if (isPlaceholder) {
|
||||
div.classList.add(placeholder);
|
||||
}
|
||||
|
||||
div.classList.add(highlighted ? highlightedState : itemSelectable);
|
||||
|
||||
if (removeItemButton) {
|
||||
|
@ -117,6 +121,7 @@ export const TEMPLATES = /** @type {Templates} */ ({
|
|||
|
||||
return div;
|
||||
},
|
||||
|
||||
choiceList({ list }, isSelectOneElement) {
|
||||
const div = Object.assign(document.createElement('div'), {
|
||||
className: list,
|
||||
|
@ -196,6 +201,7 @@ export const TEMPLATES = /** @type {Templates} */ ({
|
|||
|
||||
return div;
|
||||
},
|
||||
|
||||
input({ input, inputCloned }, placeholderValue) {
|
||||
const inp = Object.assign(document.createElement('input'), {
|
||||
type: 'text',
|
||||
|
@ -211,6 +217,7 @@ export const TEMPLATES = /** @type {Templates} */ ({
|
|||
|
||||
return inp;
|
||||
},
|
||||
|
||||
dropdown({ list, listDropdown }) {
|
||||
const div = document.createElement('div');
|
||||
|
||||
|
@ -219,6 +226,7 @@ export const TEMPLATES = /** @type {Templates} */ ({
|
|||
|
||||
return div;
|
||||
},
|
||||
|
||||
notice({ item, itemChoice, noResults, noChoices }, innerHTML, type = '') {
|
||||
const classes = [item, itemChoice];
|
||||
|
||||
|
@ -233,6 +241,7 @@ export const TEMPLATES = /** @type {Templates} */ ({
|
|||
className: classes.join(' '),
|
||||
});
|
||||
},
|
||||
|
||||
option({ label, value, customProperties, active, disabled }) {
|
||||
const opt = new Option(label, value, false, active);
|
||||
|
||||
|
|
Loading…
Reference in New Issue