Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,14 @@ Pass an array of objects:

**Usage:** The amount of items a user can input/select ("-1" indicates no limit).

### renderItems

**Type:** `Boolean` **Default:** `true`

**Input types affected:** `text`, `select-multiple`

**Usage:** Whether to render items.

### closeDropdownOnSelect

**Type:** `Boolean` | 'auto' **Default:** `auto`
Expand Down Expand Up @@ -859,7 +867,7 @@ const example = new Choices(element, {
return template(`
<div class="${getClassNames(classNames.item).join(' ')} ${getClassNames(classNames.itemChoice).join(' ')} ${
getClassNames(data.disabled ? classNames.itemDisabled : classNames.itemSelectable).join(' ')
}" data-select-text="${this.config.itemSelectText}" data-choice ${
}" data-select-text="${this.config.itemSelectText}" data-deselect-text="${this.config.itemDeselectText}" data-choice ${
data.disabled
? 'data-choice-disabled aria-disabled="true"'
: 'data-choice-selectable'
Expand Down
19 changes: 19 additions & 0 deletions public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,20 @@ <h2>Multiple select input</h2>
<option value="Choice 4" disabled>Choice 4</option>
</select>

<label for="choices-multiple-keep">Keep in dropdown without rendering items</label>
<select
class="form-control"
name="choices-multiple-default"
id="choices-multiple-keep"
placeholder="This is a placeholder"
multiple
>
<option value="Choice 1" selected>Choice 1</option>
<option value="Choice 2">Choice 2</option>
<option value="Choice 3">Choice 3</option>
<option value="Choice 4" disabled>Choice 4</option>
</select>

<label for="choices-multiple-remove-button">With remove button</label>
<select
class="form-control"
Expand Down Expand Up @@ -640,6 +654,11 @@ <h2>Form interaction</h2>
],
});

var multipleKeep = new Choices(
document.getElementById('choices-multiple-keep'),
{ renderSelectedChoices: 'always', renderItems: false }
);

var multipleDefault = new Choices(
document.getElementById('choices-multiple-groups'),
{ allowHTML: true }
Expand Down
24 changes: 24 additions & 0 deletions public/test/select-multiple/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,30 @@ <h2>Select multiple inputs</h2>
</script>
</div>

<div data-test-hook="dont-render-selected-items">
<label for="choices-remove-button">Don't render selected items</label>
<select
class="form-control"
name="dont-render-selected-items"
id="dont-render-selected-items"
multiple
>
<option value="Choice 1" selected>Choice 1</option>
<option value="Choice 2">Choice 2</option>
<option value="Choice 3">Choice 3</option>
<option value="Choice 4">Choice 4</option>
</select>
<script>
document.addEventListener('DOMContentLoaded', function() {
new Choices('#dont-render-selected-items', {
allowHTML: true,
renderSelectedChoices: true,
renderItems: false,
});
});
</script>
</div>

<div data-test-hook="remove-button">
<label for="choices-remove-button">Remove button</label>
<select
Expand Down
7 changes: 6 additions & 1 deletion src/scripts/choices.ts
Original file line number Diff line number Diff line change
Expand Up @@ -930,7 +930,7 @@
}
}

if (changes.items) {
if (changes.items && this.config.renderItems) {
this._renderItems();
}
}
Expand Down Expand Up @@ -1294,6 +1294,11 @@
});

this._triggerChange(choice.value);
} else if (this.config.renderSelectedChoices && this._isSelectMultipleElement) {
this._store.withTxn(() => {

Check failure on line 1298 in src/scripts/choices.ts

View workflow job for this annotation

GitHub Actions / lint

Delete `··`
this._removeItem(choice);

Check failure on line 1299 in src/scripts/choices.ts

View workflow job for this annotation

GitHub Actions / lint

Replace `············` with `········`
});

Check failure on line 1300 in src/scripts/choices.ts

View workflow job for this annotation

GitHub Actions / lint

Delete `··`
this._triggerChange(choice.value);

Check failure on line 1301 in src/scripts/choices.ts

View workflow job for this annotation

GitHub Actions / lint

Delete `··`
}

// We want to close the dropdown if we are dealing with a single select box
Expand Down
2 changes: 2 additions & 0 deletions src/scripts/defaults.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export const DEFAULT_CONFIG: Options = {
silent: false,
renderChoiceLimit: -1,
maxItemCount: -1,
renderItems: true,
closeDropdownOnSelect: 'auto',
singleModeForMultiSelect: false,
addChoices: false,
Expand Down Expand Up @@ -75,6 +76,7 @@ export const DEFAULT_CONFIG: Options = {
noResultsText: 'No results found',
noChoicesText: 'No choices to choose from',
itemSelectText: 'Press to select',
itemDeselectText: 'Press to deselect',
uniqueItemText: 'Only unique values can be added',
customAddItemText: 'Only values matching specific conditions can be added',
addItemText: (value: string) => `Press Enter to add <b>"${value}"</b>`,
Expand Down
20 changes: 19 additions & 1 deletion src/scripts/interfaces/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,15 @@ export interface Options {
*/
maxItemCount: number;

/**
* Whether to render items.
*
* **Input types affected:** text, text-multiple
*
* @default true
*/
renderItems: boolean;

/**
* Control how the dropdown closes after making a selection for select-one or select-multiple
*
Expand Down Expand Up @@ -507,6 +516,15 @@ export interface Options {
*/
itemSelectText: string;

/**
* The text that is shown when a user hovers over a selected choice. Set to empty to not reserve space for this text.
*
* **Input types affected:** select-multiple, select-one
*
* @default 'Press to deselect'
*/
itemDeselectText: string;

/**
* The text that is shown when a user has focus on the input but has already reached the **max item count** [https://github.com/jshjohnson/Choices#maxitemcount]. To access the max item count, pass a function with a `maxItemCount` argument (see the **default config** [https://github.com/jshjohnson/Choices#setup] for an example), otherwise pass a string.
*
Expand Down Expand Up @@ -601,7 +619,7 @@ export interface Options {
* },
* choice: (data) => {
* return template(`
* <div class="${getClassNames(classNames.item).join(' ')} ${classNames.itemChoice} ${data.disabled ? classNames.itemDisabled : classNames.itemSelectable}" data-select-text="${this.config.itemSelectText}" data-choice ${data.disabled ? 'data-choice-disabled aria-disabled="true"' : 'data-choice-selectable'} data-id="${data.id}" data-value="${data.value}" ${data.groupId ? 'role="treeitem"' : 'role="option"'}>
* <div class="${getClassNames(classNames.item).join(' ')} ${classNames.itemChoice} ${data.disabled ? classNames.itemDisabled : classNames.itemSelectable}" data-select-text="${this.config.itemSelectText}" data-deselect-text="${this.config.itemDeselectText}" data-choice ${data.disabled ? 'data-choice-disabled aria-disabled="true"' : 'data-choice-selectable'} data-id="${data.id}" data-value="${data.value}" ${data.groupId ? 'role="treeitem"' : 'role="option"'}>
* <span>&bigstar;</span> ${data.label}
* </div>
* `);
Expand Down
1 change: 1 addition & 0 deletions src/scripts/interfaces/templates.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export type TemplateOptions = Pick<
| 'removeItemLabelText'
| 'searchEnabled'
| 'labelId'
| 'itemDeselectText'
>;

export const NoticeTypes = {
Expand Down
4 changes: 4 additions & 0 deletions src/scripts/templates.ts
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,7 @@ const templates: TemplatesInterface = {
{
allowHTML,
classNames: { item, itemChoice, itemSelectable, selectedState, itemDisabled, description, placeholder },
itemDeselectText,
}: TemplateOptions,
choice: ChoiceFull,
selectText: string,
Expand Down Expand Up @@ -307,6 +308,9 @@ const templates: TemplatesInterface = {
if (selectText) {
div.dataset.selectText = selectText;
}
if (itemDeselectText) {
div.dataset.deselectText = itemDeselectText;
}
if (choice.group) {
div.dataset.groupId = `${choice.group.id}`;
}
Expand Down
17 changes: 16 additions & 1 deletion src/styles/choices.scss
Original file line number Diff line number Diff line change
Expand Up @@ -294,13 +294,28 @@ $choices-z-index: 1 !default;
text-align: right;
}
}
[data-type*="select-multiple"] & .#{$choices-selector}__item--selectable {
&[data-deselect-text] {
@media (min-width: 640px) {
&.is-selected::after {
content: attr(data-deselect-text);
}
}
}
}
.#{$choices-selector}__item--selectable {
&[data-select-text] {
@media (min-width: 640px) {
padding-right: 100px;

&::after {
&.is-selected {
background-color: $choices-primary-color;
color: #fff;
}
&:not(.is-selected)::after {
content: attr(data-select-text);
}
&::after {
font-size: $choices-font-size-sm;
opacity: 0;
position: absolute;
Expand Down
45 changes: 45 additions & 0 deletions test-e2e/tests/select-multiple.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,24 @@
describe('selecting choices', () => {
const selectedChoiceText = 'Choice 1';

test('shows deselect text on hover of selected choice', async ({ page, bundle }) => {
const suite = new SelectTestSuit(page, bundle, testUrl, testId);

Check failure on line 259 in test-e2e/tests/select-multiple.spec.ts

View workflow job for this annotation

GitHub Actions / lint

Delete `··`
await suite.startWithClick();

Check failure on line 260 in test-e2e/tests/select-multiple.spec.ts

View workflow job for this annotation

GitHub Actions / lint

Delete `··`

await suite.choices.first().hover();

Check failure on line 262 in test-e2e/tests/select-multiple.spec.ts

View workflow job for this annotation

GitHub Actions / lint

Delete `··`

const deselectText = 'Deselect';

Check failure on line 264 in test-e2e/tests/select-multiple.spec.ts

View workflow job for this annotation

GitHub Actions / lint

Delete `··`
const afterContent = await page.evaluate(({testId, deselectText}) => {

Check failure on line 265 in test-e2e/tests/select-multiple.spec.ts

View workflow job for this annotation

GitHub Actions / lint

'testId' is already declared in the upper scope on line 254 column 13

Check failure on line 265 in test-e2e/tests/select-multiple.spec.ts

View workflow job for this annotation

GitHub Actions / lint

Replace `············const·afterContent·=·await·page.evaluate(({testId,·deselectText` with `··········const·afterContent·=·await·page.evaluate(⏎············({·testId,·deselectText·`
const choice = document.querySelector(`[data-test-hook=${testId}] .choices__item--choice.is-selected`) as HTMLElement;
choice.dataset.deselectText = deselectText;
return getComputedStyle(choice, ':after').content;
},
{testId, deselectText},
);

await expect(afterContent).toContain(`\"${deselectText}\"`);
});

test('not removing selected choice from dropdown list', async ({ page, bundle }) => {
const suite = new SelectTestSuit(page, bundle, testUrl, testId);
await suite.startWithClick();
Expand Down Expand Up @@ -284,6 +302,7 @@
const suite = new SelectTestSuit(page, bundle, testUrl, testId);
await suite.startWithClick();

await suite.choices.first().click();
const count = await suite.choices.count();

for (let i = 1; i < count + 1; i++) {
Expand Down Expand Up @@ -330,6 +349,32 @@
});
});

describe('dont-render-selected-items', () => {
const testId = 'dont-render-selected-items';
describe('selecting choices', () => {
test('all available choices', async ({ page, bundle }) => {
const suite = new SelectTestSuit(page, bundle, testUrl, testId);
await suite.startWithClick();

await suite.choices.first().click();
const count = await suite.choices.count();

for (let i = 1; i < count + 1; i++) {
await suite.expectVisibleDropdown();
await suite.getChoiceWithText(`Choice ${i}`).click();
await suite.advanceClock();
await suite.expectedItemCount(0);
if (i < count) {
await expect(suite.getChoiceWithText(`Choice ${i}`)).toHaveClass(/is-selected/);
await expect(suite.selectableChoices).toHaveCount(count);
} else {
await suite.expectHiddenNotice();
}
}
});
});
});

describe('remove button', () => {
const testId = 'remove-button';
describe('on click', () => {
Expand Down
18 changes: 18 additions & 0 deletions test-e2e/tests/select-one.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,24 @@ describe(`Choices - select one`, () => {
await expect(suite.items.last()).not.toHaveText('!--');
});

test('does not show deselect text on hover of selected choice', async ({ page, bundle }) => {
const suite = new SelectTestSuit(page, bundle, testUrl, testId);
await suite.startWithClick();

await suite.choices.first().hover();

const deselectText = 'Deselect';
const afterContent = await page.evaluate(({testId, deselectText}) => {
const choice = document.querySelector(`[data-test-hook=${testId}] .choices__item--choice.is-selected`) as HTMLElement;
choice.dataset.deselectText = deselectText;
return getComputedStyle(choice, ':after').content;
},
{testId, deselectText},
);

await expect(afterContent).not.toContain(`\"${deselectText}\"`);
});

test('does not remove selected choice from dropdown list', async ({ page, bundle }) => {
const suite = new SelectTestSuit(page, bundle, testUrl, testId);
await suite.startWithClick();
Expand Down
16 changes: 11 additions & 5 deletions test/scripts/templates.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,7 @@ describe('templates', () => {
});

const itemSelectText = 'test 6';
const itemDeselectText = 'test 7';

let data;

Expand All @@ -422,10 +423,11 @@ describe('templates', () => {
class="${getClassNames(choiceOptions.classNames.item).join(' ')} ${getClassNames(
choiceOptions.classNames.itemChoice,
).join(' ')} ${getClassNames(choiceOptions.classNames.itemSelectable).join(' ')}"
data-select-text="${itemSelectText}"
data-choice
data-id="${data.id}"
data-value="${data.value}"
data-select-text="${itemSelectText}"
data-deselect-text="${itemDeselectText}"
data-choice-selectable
id="${data.elementId}"
role="option"
Expand Down Expand Up @@ -453,10 +455,11 @@ describe('templates', () => {
class="${getClassNames(choiceOptions.classNames.item).join(' ')} ${getClassNames(
choiceOptions.classNames.itemChoice,
).join(' ')} ${getClassNames(choiceOptions.classNames.itemDisabled).join(' ')}"
data-select-text="${itemSelectText}"
data-choice
data-id="${data.id}"
data-value="${data.value}"
data-select-text="${itemSelectText}"
data-deselect-text="${itemDeselectText}"
data-choice-disabled
aria-disabled="true"
id="${data.elementId}"
Expand Down Expand Up @@ -487,10 +490,11 @@ describe('templates', () => {
).join(
' ',
)} ${choiceOptions.classNames.selectedState} ${getClassNames(choiceOptions.classNames.itemSelectable).join(' ')}"
data-select-text="${itemSelectText}"
data-choice
data-id="${data.id}"
data-value="${data.value}"
data-select-text="${itemSelectText}"
data-deselect-text="${itemDeselectText}"
data-choice-selectable
id="${data.elementId}"
role="option"
Expand Down Expand Up @@ -520,10 +524,11 @@ describe('templates', () => {
).join(
' ',
)} ${choiceOptions.classNames.placeholder} ${getClassNames(choiceOptions.classNames.itemSelectable).join(' ')}"
data-select-text="${itemSelectText}"
data-choice
data-id="${data.id}"
data-value="${data.value}"
data-select-text="${itemSelectText}"
data-deselect-text="${itemDeselectText}"
data-choice-selectable
id="${data.elementId}"
role="option"
Expand Down Expand Up @@ -551,10 +556,11 @@ describe('templates', () => {
class="${getClassNames(choiceOptions.classNames.item).join(' ')} ${getClassNames(
choiceOptions.classNames.itemChoice,
).join(' ')} ${getClassNames(choiceOptions.classNames.itemSelectable).join(' ')}"
data-select-text="${itemSelectText}"
data-choice
data-id="${data.id}"
data-value="${data.value}"
data-select-text="${itemSelectText}"
data-deselect-text="${itemDeselectText}"
data-group-id="${data.groupId}"
data-choice-selectable
id="${data.elementId}"
Expand Down
Loading