Skip to content

Commit 4a5f7e4

Browse files
authored
feat: add new closeOnTab option (#387)
1 parent 7c4a09d commit 4a5f7e4

File tree

7 files changed

+247
-1
lines changed

7 files changed

+247
-1
lines changed

packages/demo/src/app-routing.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ import Options39 from './options/options39.js';
7171
import Options40 from './options/options40.js';
7272
import Options41 from './options/options41.js';
7373
import Options42 from './options/options42.js';
74+
import Options43 from './options/options43.js';
7475

7576
export const navbarRouting = [
7677
{ name: 'getting-started', view: '/src/getting-started.html', viewModel: GettingStarted, title: 'Getting Started' },
@@ -144,6 +145,7 @@ export const exampleRouting = [
144145
{ name: 'options40', view: '/src/options/options40.html', viewModel: Options40, title: 'Pre-Filter Data' },
145146
{ name: 'options41', view: '/src/options/options41.html', viewModel: Options41, title: 'Pre-Sort Data' },
146147
{ name: 'options42', view: '/src/options/options42.html', viewModel: Options42, title: 'Lazy Load Data' },
148+
{ name: 'options43', view: '/src/options/options43.html', viewModel: Options43, title: 'Close on Tab' },
147149
],
148150
},
149151
{

packages/demo/src/options/options38.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<div class="example15-container">
1+
<div class="example38-container">
22
<div class="row mb-2">
33
<div class="col-md-12 title-desc">
44
<h2 class="bd-title">
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
<div class="row mb-2">
2+
<div class="col-md-12 title-desc">
3+
<h2 class="bd-title">
4+
Close on Tab
5+
<span class="float-end links">
6+
Code <span class="fa fa-link"></span>
7+
<span class="small">
8+
<a target="_blank" href="https://github.com/ghiscoding/multiple-select-vanilla/blob/main/packages/demo/src/options/options43.html"
9+
>html</a
10+
>
11+
|
12+
<a target="_blank" href="https://github.com/ghiscoding/multiple-select-vanilla/blob/main/packages/demo/src/options/options43.ts"
13+
>ts</a
14+
>
15+
</span>
16+
</span>
17+
</h2>
18+
</div>
19+
</div>
20+
21+
<div>
22+
<div class="mb-3 row">
23+
<label class="col-sm-2">Single Select</label>
24+
25+
<div class="col-sm-10">
26+
<select id="single" class="select full-width" data-test="single">
27+
<option value="1">January</option>
28+
<option value="2">February</option>
29+
<option value="3">March</option>
30+
<option value="4">April</option>
31+
<option data-divider="true"></option>
32+
<option value="5">May</option>
33+
<option value="6">June</option>
34+
<option value="7">July</option>
35+
<option value="8">August</option>
36+
<option data-divider="true"></option>
37+
<option value="9">September</option>
38+
<option value="10">October</option>
39+
<option value="11">November</option>
40+
<option value="12">December</option>
41+
</select>
42+
</div>
43+
</div>
44+
45+
<div class="mb-3 row">
46+
<label class="col-sm-2">Single Radio</label>
47+
48+
<div class="col-sm-10">
49+
<select id="single" class="radio full-width" data-test="radio">
50+
<option value="1">January</option>
51+
<option value="2" selected>February</option>
52+
<option value="3">March</option>
53+
<option value="4">April</option>
54+
<option data-divider="true"></option>
55+
<option value="5">May</option>
56+
<option value="6">June</option>
57+
<option value="7">July</option>
58+
<option value="8">August</option>
59+
<option data-divider="true"></option>
60+
<option value="9">September</option>
61+
<option value="10">October</option>
62+
<option value="11">November</option>
63+
<option value="12">December</option>
64+
</select>
65+
</div>
66+
</div>
67+
68+
<div class="mb-3 row">
69+
<label class="col-sm-2">Multiple Select</label>
70+
71+
<div class="col-sm-10">
72+
<select id="multiple" class="select full-width" data-test="multiple" multiple>
73+
<option value="1">January</option>
74+
<option value="2">February</option>
75+
<option value="3">March</option>
76+
<option value="4">April</option>
77+
<option data-divider="true"></option>
78+
<option value="5">May</option>
79+
<option value="6">June</option>
80+
<option value="7">July</option>
81+
<option value="8">August</option>
82+
<option data-divider="true"></option>
83+
<option value="9">September</option>
84+
<option value="10">October</option>
85+
<option value="11">November</option>
86+
<option value="12">December</option>
87+
</select>
88+
</div>
89+
</div>
90+
91+
<div class="mb-3 row">
92+
<label class="col-sm-2">Group Select</label>
93+
94+
<div class="col-sm-10">
95+
<select id="group" class="select full-width" data-test="group" multiple>
96+
<optgroup label="Group 1" disabled="disabled">
97+
<option value="1" selected>Option 1</option>
98+
<option value="2">Option 2</option>
99+
<option value="3">Option 3</option>
100+
</optgroup>
101+
<optgroup label="Group 2">
102+
<option value="4">Option 4</option>
103+
<option value="5" selected>Option 5</option>
104+
<option value="6">Option 6</option>
105+
</optgroup>
106+
<optgroup label="Group 3">
107+
<option value="7" disabled="disabled">Option 7</option>
108+
<option value="8">Option 8</option>
109+
<option value="9">Option 9</option>
110+
</optgroup>
111+
</select>
112+
</div>
113+
</div>
114+
115+
<div class="mb-3 row">
116+
<label class="col-sm-2">Data Select 1</label>
117+
118+
<div class="col-sm-10">
119+
<select id="data-select" class="data-select full-width" data-test="data1" multiple></select>
120+
</div>
121+
</div>
122+
</div>
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import { type MultipleSelectInstance, multipleSelect } from 'multiple-select-vanilla';
2+
3+
import './options38.scss';
4+
5+
export default class Example {
6+
pageContentElm: HTMLDivElement | null = null;
7+
ms1?: MultipleSelectInstance;
8+
ms2?: MultipleSelectInstance;
9+
ms3?: MultipleSelectInstance;
10+
ms4?: MultipleSelectInstance;
11+
ms5?: MultipleSelectInstance;
12+
13+
mount() {
14+
this.pageContentElm = document.querySelector<HTMLDivElement>('.panel-wm-content');
15+
16+
this.ms1 = multipleSelect('select[data-test=single]', { closeOnTab: true }) as MultipleSelectInstance;
17+
this.ms2 = multipleSelect('select[data-test=radio]', { closeOnTab: true, singleRadio: true }) as MultipleSelectInstance;
18+
this.ms3 = multipleSelect('select[data-test=multiple]', { closeOnTab: true }) as MultipleSelectInstance;
19+
this.ms4 = multipleSelect('select[data-test=group]', { closeOnTab: true }) as MultipleSelectInstance;
20+
this.ms5 = multipleSelect('select[data-test=data1]', {
21+
closeOnTab: true,
22+
dataTest: 'select1',
23+
filter: true,
24+
showOkButton: true,
25+
showClear: true,
26+
showSearchClear: true,
27+
data: [
28+
{
29+
value: 1,
30+
text: 'Option 1',
31+
},
32+
{
33+
value: 2,
34+
text: 'Option 2',
35+
},
36+
{
37+
value: 3,
38+
text: 'Option 3',
39+
},
40+
{
41+
divider: true,
42+
},
43+
{
44+
value: 4,
45+
text: 'Option 4',
46+
},
47+
{
48+
value: 5,
49+
text: 'Option 5',
50+
},
51+
{
52+
value: 6,
53+
text: 'Option 6',
54+
},
55+
],
56+
}) as MultipleSelectInstance;
57+
}
58+
59+
unmount() {
60+
// destroy ms instance(s) to avoid DOM leaks
61+
this.ms1?.destroy();
62+
this.ms2?.destroy();
63+
this.ms3?.destroy();
64+
this.ms4?.destroy();
65+
this.ms5?.destroy();
66+
this.ms1 = undefined;
67+
this.ms2 = undefined;
68+
this.ms3 = undefined;
69+
this.ms4 = undefined;
70+
this.ms5 = undefined;
71+
}
72+
}

packages/multiple-select-vanilla/src/MultipleSelectInstance.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,9 @@ export class MultipleSelectInstance {
283283
this._bindEventService.bind(this.dropElm, 'keyup', ((e: KeyboardEvent) => {
284284
if (e.code === 'Tab') {
285285
this.options.onBlur(e);
286+
if (this.options.closeOnTab) {
287+
this.close('blur');
288+
}
286289
}
287290
}) as EventListener);
288291
}

packages/multiple-select-vanilla/src/models/multipleSelectOption.interface.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,9 @@ export interface MultipleSelectOption extends MultipleSelectLocale {
7373
/** The class prefix of select. */
7474
classPrefix?: string;
7575

76+
/** Should we close the drop when Tab key is pressed. */
77+
closeOnTab?: boolean;
78+
7679
/** HTML container to use for the drop menu, e.g. 'body' */
7780
container?: string | HTMLElement | null;
7881

playwright/e2e/options43.spec.ts

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import { expect, test } from '@playwright/test';
2+
3+
test.describe('Options 43 - Close on Tab', () => {
4+
test('multiple select to close on Tab', async ({ page }) => {
5+
await page.goto('#/options43');
6+
7+
// 1st Select
8+
await page.locator('div[data-test=single] .ms-choice').filter({ hasText: 'January' }).click();
9+
await expect(page.locator('div[data-test=single] .ms-drop')).toBeVisible();
10+
const li1LabelElms = await page.locator('div[data-test=single] li.hide-radio');
11+
await expect(li1LabelElms).toHaveCount(12);
12+
await page.keyboard.press('Tab');
13+
await expect(page.locator('div[data-test=single] .ms-drop')).not.toBeVisible();
14+
15+
// 2nd Select
16+
await page.locator('div[data-test=radio] .ms-choice').filter({ hasText: 'February' }).click();
17+
const li2LabelElms = await page.locator('div[data-test=radio] li[role=option]');
18+
await expect(page.locator('div[data-test=radio] .ms-drop')).toBeVisible();
19+
await expect(li2LabelElms).toHaveCount(12);
20+
await page.keyboard.press('Tab');
21+
await expect(page.locator('div[data-test=radio] .ms-drop')).not.toBeVisible();
22+
23+
// 3rd Select
24+
await page.locator('div[data-test=multiple] .ms-choice').click();
25+
const li3LabelElms = await page.locator('div[data-test=multiple] li:not(.option-divider)');
26+
await expect(page.locator('div[data-test=multiple] .ms-drop')).toBeVisible();
27+
await expect(li3LabelElms).toHaveCount(13);
28+
await page.keyboard.press('Tab');
29+
await expect(page.locator('div[data-test=multiple] .ms-drop')).not.toBeVisible();
30+
31+
// 4th Select
32+
await page.getByRole('button', { name: '[Group 1: Option 1], [Group 2: Option 5]' }).click();
33+
await expect(page.locator('div[data-test=group] .ms-drop')).toBeVisible();
34+
await page.keyboard.press('Tab');
35+
await expect(page.locator('div[data-test=group] .ms-drop')).not.toBeVisible();
36+
37+
// 5th Select
38+
await page.locator('div[data-test=data1] .ms-choice').click();
39+
await expect(page.locator('div[data-test=data1] .ms-drop')).toBeVisible();
40+
await page.keyboard.press('Tab');
41+
await expect(page.locator('div[data-test=data1] .ms-drop')).not.toBeVisible();
42+
// });
43+
});
44+
});

0 commit comments

Comments
 (0)