Skip to content

Commit 90a1de2

Browse files
iropoloiropolo
andauthored
chore(combobox-multi-select): DLT-2930 moved from recipe to component (#1112)
Co-authored-by: iropolo <ignacio.ropolo@dialpad.com>
1 parent 5c452ec commit 90a1de2

File tree

13 files changed

+267
-21
lines changed

13 files changed

+267
-21
lines changed

apps/dialtone-documentation/docs/_data/site-nav.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -609,6 +609,10 @@
609609
"text": "Combobox",
610610
"link": "/components/combobox.html"
611611
},
612+
{
613+
"text": "Combobox Multi-Select",
614+
"link": "/components/combobox-multi-select.html"
615+
},
612616
{
613617
"text": "Datepicker",
614618
"link": "/components/datepicker.html"
Lines changed: 241 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,241 @@
1+
---
2+
title: Combobox Multi-Select
3+
description: Select allows users to make a single selection or multiple selections from a list of options.
4+
status: ready
5+
thumb: true
6+
# image: assets/images/components/combobox-multi-select.png
7+
storybook: https://dialtone.dialpad.com/vue/?path=/story/components-combobox-multi-select--default
8+
---
9+
10+
<code-well-header class="d-d-block">
11+
<dt-combobox-multi-select
12+
ref="example-combobox-multi-select"
13+
label="Label Text"
14+
:selected-items="selectedItems"
15+
@input="onComboboxInput"
16+
@select="onComboboxSelect"
17+
@remove="onComboboxRemove"
18+
>
19+
<template #list>
20+
<ul class="d-ps-relative d-stack2 d-m4 d-px0">
21+
<dt-list-item
22+
v-for="(item, i) in items"
23+
:key="item.id"
24+
role="option"
25+
navigation-type="arrow-keys"
26+
@click="onComboboxSelect(i)"
27+
>
28+
{{ item.value }}
29+
<template #right>
30+
<span class="d-fc-secondary">{{ item.type }}</span>
31+
</template>
32+
</dt-list-item>
33+
</ul>
34+
</template>
35+
</dt-combobox-multi-select>
36+
</code-well-header>
37+
38+
<code-example-tabs
39+
vueCode='
40+
<dt-combobox-multi-select
41+
ref="comboboxMultiSelect"
42+
label="Label Text"
43+
:selected-items="selectedItems"
44+
@input="onInput"
45+
@select="onSelect"
46+
@remove="onRemove"
47+
>
48+
<template #list>
49+
<ul class="d-ps-relative d-stack2 d-m4 d-px0">
50+
<dt-list-item
51+
v-for="(item, i) in items"
52+
:key="item.id"
53+
role="option"
54+
navigation-type="arrow-keys"
55+
@click="onSelect(i)"
56+
>
57+
{{ item.value }}
58+
<template #right>
59+
<span class="d-fc-secondary">{{ item.type }}</span>
60+
</template>
61+
</dt-list-item>
62+
</ul>
63+
</template>
64+
</dt-combobox-multi-select>
65+
'
66+
/>
67+
68+
## Usage
69+
70+
The Combobox Multi-Select component combines an input element with a dropdown list, allowing users to select multiple items. Selected items appear as chips within the input field.
71+
72+
### Closing the list after selection
73+
74+
When not passing `showList` and `hasSuggestionList` is `true`, to close the list with the `select` event, use the `closeComboboxList` method:
75+
76+
```javascript
77+
methods: {
78+
onSelect (i) {
79+
this.$refs.comboboxMultiSelect.closeComboboxList();
80+
},
81+
}
82+
```
83+
84+
## With Max Selected Validation
85+
86+
Adds validation for max selection. Make sure to provide the following props:
87+
88+
- `maxSelected` the maximum number of selections you can make. 0 is unlimited
89+
- `maxSelectedMessage` should be the message that shown if max selection has been reached
90+
91+
<code-well-header class="d-d-block">
92+
<dt-combobox-multi-select
93+
ref="example-max-select"
94+
label="Label Text"
95+
description="Select up to 2 options."
96+
:selected-items="maxSelectedItems"
97+
:max-selected="2"
98+
:max-selected-message="[{ message: 'More than 2 selected', type: 'error' }]"
99+
@input="onMaxSelectInput"
100+
@select="onMaxSelectSelect"
101+
@remove="onMaxSelectRemove"
102+
@max-selected="onMaxSelected"
103+
>
104+
<template #list>
105+
<ul class="d-ps-relative d-stack2 d-m4 d-px0">
106+
<dt-list-item
107+
v-for="(item, i) in maxSelectItems"
108+
:key="item.id"
109+
role="option"
110+
navigation-type="arrow-keys"
111+
@click="onMaxSelectSelect(i)"
112+
>
113+
{{ item.value }}
114+
<template #right>
115+
<span class="d-fc-secondary">{{ item.type }}</span>
116+
</template>
117+
</dt-list-item>
118+
</ul>
119+
</template>
120+
</dt-combobox-multi-select>
121+
</code-well-header>
122+
123+
<code-example-tabs
124+
vueCode='
125+
<dt-combobox-multi-select
126+
ref="comboboxMultiSelect"
127+
label="Label Text"
128+
description="Select up to 2 options."
129+
:selected-items="selectedItems"
130+
:max-selected="2"
131+
:max-selected-message="[{ message: &apos;More than 2 selected&apos;, type: &apos;error&apos; }]"
132+
@input="onInput"
133+
@select="onSelect"
134+
@remove="onRemove"
135+
@max-selected="onMaxSelected"
136+
>
137+
<template #list>
138+
<ul class="d-ps-relative d-stack2 d-m4 d-px0">
139+
<dt-list-item
140+
v-for="(item, i) in items"
141+
:key="item.id"
142+
role="option"
143+
navigation-type="arrow-keys"
144+
@click="onSelect(i)"
145+
>
146+
{{ item.value }}
147+
<template #right>
148+
<span class="d-fc-secondary">{{ item.type }}</span>
149+
</template>
150+
</dt-list-item>
151+
</ul>
152+
</template>
153+
</dt-combobox-multi-select>
154+
'
155+
/>
156+
157+
## Vue API
158+
159+
<component-vue-api component-name="comboboxmultiselect" />
160+
161+
## Accessibility
162+
163+
A screen reader visible only close button is added by default.
164+
165+
### Keyboard Support
166+
167+
- User can navigate between chips pressing the `LEFT` and `RIGHT` key.
168+
- Pressing `LEFT` key when you have chips in the input and you are at the start of the text would select the last chip.
169+
- Pressing `RIGHT` key when you are at the last chip would focus on the start of the input.
170+
- Pressing `BACKSPACE` key would focus the chip.
171+
- When a chip is focused, pressing `BACKSPACE` or `DELETE` key would remove the chip.
172+
- User can navigate the popover list pressing `UP` and `DOWN` key.
173+
174+
See full [Keyboard Support](/components/popover.html#keyboard-support) for popover list.
175+
176+
<script setup>
177+
import { ref } from 'vue';
178+
179+
const ITEMS_LIST_DATA = [
180+
{ id: 'item1', value: 'item1', type: 'MAINLINE' },
181+
{ id: 'item2', value: 'item2', type: 'MAINLINE' },
182+
{ id: 'item3', value: 'item3', type: 'MAINLINE' },
183+
{ id: 'item4', value: 'item4', type: 'MAINLINE' },
184+
{ id: 'item5', value: 'item5', type: 'MAINLINE' },
185+
{ id: 'item6', value: 'item6', type: 'MAINLINE' },
186+
{ id: 'item7', value: 'item7', type: 'MAINLINE' },
187+
{ id: 'item8', value: 'item8', type: 'Other' },
188+
];
189+
190+
const items = ref([...ITEMS_LIST_DATA]);
191+
const selectedItems = ref([]);
192+
193+
const maxSelectItems = ref([...ITEMS_LIST_DATA]);
194+
const maxSelectedItems = ref(['item1', 'item2', 'item3']);
195+
196+
function onComboboxInput (value) {
197+
items.value = ITEMS_LIST_DATA.filter(item => item.value.includes(value));
198+
}
199+
200+
function onComboboxSelect (i) {
201+
if (items.value[i]) {
202+
const item = items.value[i].value;
203+
if (!selectedItems.value.includes(item)) {
204+
selectedItems.value.push(item);
205+
}
206+
items.value = [...ITEMS_LIST_DATA];
207+
}
208+
}
209+
210+
function onComboboxRemove (item) {
211+
const index = selectedItems.value.indexOf(item);
212+
if (index >= 0) {
213+
selectedItems.value.splice(index, 1);
214+
}
215+
}
216+
217+
function onMaxSelectInput (value) {
218+
maxSelectItems.value = ITEMS_LIST_DATA.filter(item => item.value.includes(value));
219+
}
220+
221+
function onMaxSelectSelect (i) {
222+
if (maxSelectItems.value[i]) {
223+
const item = maxSelectItems.value[i].value;
224+
if (!maxSelectedItems.value.includes(item)) {
225+
maxSelectedItems.value.push(item);
226+
}
227+
maxSelectItems.value = [...ITEMS_LIST_DATA];
228+
}
229+
}
230+
231+
function onMaxSelectRemove (item) {
232+
const index = maxSelectedItems.value.indexOf(item);
233+
if (index >= 0) {
234+
maxSelectedItems.value.splice(index, 1);
235+
}
236+
}
237+
238+
function onMaxSelected () {
239+
console.log('Max selected reached');
240+
}
241+
</script>

packages/dialtone-css/lib/build/less/recipes/combobox_multi_select.less renamed to packages/dialtone-css/lib/build/less/components/combobox-multi-select.less

File renamed without changes.

packages/dialtone-css/lib/build/less/dialtone.less

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
@import 'components/emoji';
6060
@import 'components/emoji-text-wrapper';
6161
@import 'components/emoji-picker';
62+
@import 'components/combobox-multi-select';
6263

6364
// -- UTILITIES
6465
@import 'utilities/backgrounds';
@@ -79,7 +80,6 @@
7980
@import 'recipes/callbar_button_with_popover';
8081
@import 'recipes/callbar_button_with_dropdown';
8182
@import 'recipes/callbox';
82-
@import 'recipes/combobox_multi_select';
8383
@import 'recipes/combobox_with_popover';
8484
@import 'recipes/contact_info';
8585
@import 'recipes/editor';

packages/dialtone-vue/recipes/comboboxes/combobox_multi_select/combobox_multi_select.mdx renamed to packages/dialtone-vue/components/combobox_multi_select/combobox_multi_select.mdx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,13 +37,13 @@ See full [Keyboard Support](?path=/docs/components-popover--default#keyboard-sup
3737
### Import
3838

3939
```jsx
40-
import { DtRecipeComboboxMultiSelect } from '@dialpad/dialtone-vue';
40+
import { DtComboboxMultiSelect } from '@dialpad/dialtone-vue';
4141
```
4242

4343
### Basic usage
4444

4545
```html
46-
<dt-recipe-combobox-multi-select
46+
<dt-combobox-multi-select
4747
ref="comboboxMultiSelect"
4848
:loading-message="loadingMessage"
4949
:selected-items="selectedItems"
@@ -71,7 +71,7 @@ import { DtRecipeComboboxMultiSelect } from '@dialpad/dialtone-vue';
7171
</dt-list-item>
7272
</ul>
7373
</template>
74-
</dt-recipe-combobox-multi-select>
74+
</dt-combobox-multi-select>
7575
```
7676

7777
When not passing `showList` and `hasSuggestionList` is `true`,
@@ -94,7 +94,7 @@ Adds validation for max selection. Make sure to provide the following props:
9494
- `maxSelectedMessage` should be the message that shown if max selection has been reached
9595

9696
```html
97-
<dt-recipe-combobox-multi-select
97+
<dt-combobox-multi-select
9898
ref="comboboxMultiSelect"
9999
:selected-items="selectedItems"
100100
:max-selected="maxSelected"
@@ -123,5 +123,5 @@ Adds validation for max selection. Make sure to provide the following props:
123123
</dt-list-item>
124124
</ul>
125125
</template>
126-
</dt-recipe-combobox-multi-select>
126+
</dt-combobox-multi-select>
127127
```

packages/dialtone-vue/recipes/comboboxes/combobox_multi_select/combobox_multi_select.stories.js renamed to packages/dialtone-vue/components/combobox_multi_select/combobox_multi_select.stories.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import { action } from 'storybook/actions';
22
import { createTemplateFromVueFile } from '@/common/storybook_utils';
3-
import DtRecipeComboboxMultiSelect from './combobox_multi_select.vue';
3+
import DtComboboxMultiSelect from './combobox_multi_select.vue';
44

5-
import DtRecipeComboboxMultiSelectDefaultTemplate from './combobox_multi_select_default.story.vue';
5+
import DtComboboxMultiSelectDefaultTemplate from './combobox_multi_select_default.story.vue';
66
import { MULTI_SELECT_SIZES } from './combobox_multi_select_constants';
77
import { ITEMS_LIST_DATA } from './combobox_multi_select_story_constants';
88

@@ -140,8 +140,8 @@ export const argTypesData = {
140140

141141
// Story Collection
142142
export default {
143-
title: 'Recipes/Comboboxes/Combobox Multi-Select',
144-
component: DtRecipeComboboxMultiSelect,
143+
title: 'Components/Combobox Multi-Select',
144+
component: DtComboboxMultiSelect,
145145
args: argsData,
146146
argTypes: argTypesData,
147147
excludeStories: /.*Data$/,
@@ -151,7 +151,7 @@ export default {
151151
const Template = (args, { argTypes }) => createTemplateFromVueFile(
152152
args,
153153
argTypes,
154-
DtRecipeComboboxMultiSelectDefaultTemplate,
154+
DtComboboxMultiSelectDefaultTemplate,
155155
);
156156

157157
export const Default = {

packages/dialtone-vue/recipes/comboboxes/combobox_multi_select/combobox_multi_select.test.js renamed to packages/dialtone-vue/components/combobox_multi_select/combobox_multi_select.test.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { mount } from '@vue/test-utils';
2-
import DtRecipeComboboxMultiSelect from './combobox_multi_select.vue';
2+
import DtComboboxMultiSelect from './combobox_multi_select.vue';
33
import { VALIDATION_MESSAGE_TYPES } from '@/common/constants';
44
import { flushPromises } from '@/common/utils';
55
import SrOnlyCloseButtonComponent from '@/common/sr_only_close_button.vue';
@@ -10,7 +10,7 @@ const baseProps = {
1010
label: 'Label Text',
1111
};
1212

13-
describe('DtRecipeComboboxMultiSelect Tests', () => {
13+
describe('DtComboboxMultiSelect Tests', () => {
1414
// Wrappers
1515
let wrapper;
1616
let chips;
@@ -39,7 +39,7 @@ describe('DtRecipeComboboxMultiSelect Tests', () => {
3939
};
4040

4141
const _setWrappers = () => {
42-
wrapper = mount(DtRecipeComboboxMultiSelect, {
42+
wrapper = mount(DtComboboxMultiSelect, {
4343
props,
4444
slots,
4545
attrs,

packages/dialtone-vue/recipes/comboboxes/combobox_multi_select/combobox_multi_select.vue renamed to packages/dialtone-vue/components/combobox_multi_select/combobox_multi_select.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ import {
135135
136136
export default {
137137
compatConfig: { MODE: 3 },
138-
name: 'DtRecipeComboboxMultiSelect',
138+
name: 'DtComboboxMultiSelect',
139139
140140
components: {
141141
DtRecipeComboboxWithPopover,

packages/dialtone-vue/recipes/comboboxes/combobox_multi_select/combobox_multi_select_constants.js renamed to packages/dialtone-vue/components/combobox_multi_select/combobox_multi_select_constants.js

File renamed without changes.

0 commit comments

Comments
 (0)