Skip to content

Commit b404579

Browse files
authored
feat: add option to always show all chips for selected items (#6515)
1 parent 735f5d9 commit b404579

13 files changed

+163
-1
lines changed

packages/multi-select-combo-box/src/vaadin-multi-select-combo-box-container.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ registerStyles(
1414
display: flex;
1515
width: 100%;
1616
}
17+
18+
:host([all-chips-visible]) #wrapper {
19+
flex-wrap: wrap;
20+
}
1721
`,
1822
{
1923
moduleId: 'vaadin-multi-select-combo-box-container-styles',
@@ -49,6 +53,20 @@ class MultiSelectComboBoxContainer extends InputContainer {
4953
}
5054
return memoizedTemplate;
5155
}
56+
57+
static get properties() {
58+
return {
59+
/**
60+
* Set true to not collapse selected items chips into the overflow
61+
* chip and instead always show them, causing input field to grow.
62+
* @attr {boolean} all-chips-visible
63+
*/
64+
allChipsVisible: {
65+
type: Boolean,
66+
reflectToAttribute: true,
67+
},
68+
};
69+
}
5270
}
5371

5472
defineCustomElement(MultiSelectComboBoxContainer);

packages/multi-select-combo-box/src/vaadin-multi-select-combo-box.d.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,14 @@ export interface MultiSelectComboBoxEventMap<TItem> extends HTMLElementEventMap
171171
* @fires {CustomEvent} validated - Fired whenever the field is validated.
172172
*/
173173
declare class MultiSelectComboBox<TItem = ComboBoxDefaultItem> extends HTMLElement {
174+
/**
175+
* Set to true to not collapse selected items chips into the overflow
176+
* chip and instead always show them all, causing input field to grow
177+
* and wrap into multiple lines when width is limited.
178+
* @attr {boolean} all-chips-visible
179+
*/
180+
allChipsVisible: boolean;
181+
174182
/**
175183
* When true, the user can input a value that is not present in the items list.
176184
* @attr {boolean} allow-custom-value

packages/multi-select-combo-box/src/vaadin-multi-select-combo-box.js

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,14 @@ const multiSelectComboBox = css`
4545
flex-basis: 0;
4646
padding: 0;
4747
}
48+
49+
:host([all-chips-visible]) #chips {
50+
display: contents;
51+
}
52+
53+
:host([all-chips-visible]) [class$='container'] {
54+
width: fit-content;
55+
}
4856
`;
4957

5058
registerStyles('vaadin-multi-select-combo-box', [inputFieldShared, multiSelectComboBox], {
@@ -177,6 +185,7 @@ class MultiSelectComboBox extends ResizeMixin(InputControlMixin(ThemableMixin(El
177185
>
178186
<vaadin-multi-select-combo-box-container
179187
part="input-field"
188+
all-chips-visible="[[allChipsVisible]]"
180189
readonly="[[readonly]]"
181190
disabled="[[disabled]]"
182191
invalid="[[invalid]]"
@@ -213,6 +222,19 @@ class MultiSelectComboBox extends ResizeMixin(InputControlMixin(ThemableMixin(El
213222

214223
static get properties() {
215224
return {
225+
/**
226+
* Set to true to not collapse selected items chips into the overflow
227+
* chip and instead always show them all, causing input field to grow
228+
* and wrap into multiple lines when width is limited.
229+
* @attr {boolean} all-chips-visible
230+
*/
231+
allChipsVisible: {
232+
type: Boolean,
233+
value: false,
234+
reflectToAttribute: true,
235+
observer: '_allChipsVisibleChanged',
236+
},
237+
216238
/**
217239
* Set true to prevent the overlay from opening automatically.
218240
* @attr {boolean} auto-open-disabled
@@ -674,6 +696,13 @@ class MultiSelectComboBox extends ResizeMixin(InputControlMixin(ThemableMixin(El
674696
super._delegateAttribute(name, value);
675697
}
676698

699+
/** @private */
700+
_allChipsVisibleChanged(visible, oldVisible) {
701+
if (visible || oldVisible) {
702+
this.__updateChips();
703+
}
704+
}
705+
677706
/**
678707
* Setting clear button visible reduces total space available
679708
* for rendering chips, and making it hidden increases it.
@@ -918,7 +947,8 @@ class MultiSelectComboBox extends ResizeMixin(InputControlMixin(ThemableMixin(El
918947
const chip = this.__createChip(items[i]);
919948
this.insertBefore(chip, refNode);
920949

921-
if (this.$.chips.clientWidth > remainingWidth) {
950+
// If all the chips are visible, no need to measure remaining width
951+
if (!this.allChipsVisible && this.$.chips.clientWidth > remainingWidth) {
922952
chip.remove();
923953
break;
924954
}

packages/multi-select-combo-box/test/chips.test.js

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -411,4 +411,54 @@ describe('chips', () => {
411411
});
412412
});
413413
});
414+
415+
describe('allChipsVisible', () => {
416+
let overflow;
417+
418+
beforeEach(async () => {
419+
comboBox.style.width = '250px';
420+
await nextResize(comboBox);
421+
overflow = getChips(comboBox)[0];
422+
});
423+
424+
it('should not show overflow chip when allChipsVisible is set to true', async () => {
425+
comboBox.allChipsVisible = true;
426+
comboBox.selectedItems = ['apple', 'banana'];
427+
await nextRender();
428+
expect(getChips(comboBox).length).to.equal(3);
429+
expect(overflow.hasAttribute('hidden')).to.be.true;
430+
});
431+
432+
it('should show overflow chip when allChipsVisible is set to false', async () => {
433+
comboBox.allChipsVisible = true;
434+
comboBox.selectedItems = ['apple', 'banana'];
435+
await nextRender();
436+
437+
comboBox.allChipsVisible = false;
438+
await nextRender();
439+
expect(getChips(comboBox).length).to.equal(2);
440+
expect(overflow.hasAttribute('hidden')).to.be.false;
441+
});
442+
443+
it('should update chips when allChipsVisible is set after selectedItems', async () => {
444+
comboBox.selectedItems = ['apple', 'banana'];
445+
await nextRender();
446+
expect(getChips(comboBox).length).to.equal(2);
447+
expect(overflow.hasAttribute('hidden')).to.be.false;
448+
449+
comboBox.allChipsVisible = true;
450+
await nextRender();
451+
expect(getChips(comboBox).length).to.equal(3);
452+
expect(overflow.hasAttribute('hidden')).to.be.true;
453+
});
454+
455+
it('should wrap chips and increase input field height if chips do not fit', async () => {
456+
const inputField = comboBox.shadowRoot.querySelector('[part="input-field"]');
457+
const height = inputField.clientHeight;
458+
comboBox.allChipsVisible = true;
459+
comboBox.selectedItems = ['apple', 'banana', 'lemon', 'orange'];
460+
await nextRender();
461+
expect(inputField.clientHeight).to.be.greaterThan(height);
462+
});
463+
});
414464
});

packages/multi-select-combo-box/test/typings/multi-select-combo-box.types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ assertType<string | null | undefined>(narrowedComboBox.label);
105105
assertType<boolean>(narrowedComboBox.required);
106106
assertType<string>(narrowedComboBox.overlayClass);
107107
assertType<boolean>(narrowedComboBox.groupSelectedItems);
108+
assertType<boolean>(narrowedComboBox.allChipsVisible);
108109

109110
// Mixins
110111
assertType<ControllerMixinClass>(narrowedComboBox);

packages/multi-select-combo-box/test/visual/lumo/multi-select-combo-box.test.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,22 @@ describe('multi-select-combo-box', () => {
120120
});
121121
});
122122

123+
describe('all chips visible', () => {
124+
beforeEach(() => {
125+
element.selectedItems = [...element.items];
126+
element.allChipsVisible = true;
127+
});
128+
129+
it('all chips visible', async () => {
130+
await visualDiff(div, 'all-chips-visible');
131+
});
132+
133+
it('all chips visible max width', async () => {
134+
element.style.maxWidth = '250px';
135+
await visualDiff(div, 'all-chips-visible-max-width');
136+
});
137+
});
138+
123139
describe('opened', () => {
124140
beforeEach(() => {
125141
div.style.height = '200px';

packages/multi-select-combo-box/test/visual/material/multi-select-combo-box.test.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,22 @@ describe('multi-select-combo-box', () => {
120120
});
121121
});
122122

123+
describe('all chips visible', () => {
124+
beforeEach(() => {
125+
element.selectedItems = [...element.items];
126+
element.allChipsVisible = true;
127+
});
128+
129+
it('all chips visible', async () => {
130+
await visualDiff(div, 'all-chips-visible');
131+
});
132+
133+
it('all chips visible max width', async () => {
134+
element.style.maxWidth = '250px';
135+
await visualDiff(div, 'all-chips-visible-max-width');
136+
});
137+
});
138+
123139
describe('opened', () => {
124140
beforeEach(() => {
125141
div.style.height = '200px';

packages/multi-select-combo-box/theme/lumo/vaadin-multi-select-combo-box-styles.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,16 @@ registerStyles(
4848
{ moduleId: 'lumo-multi-select-combo-box-overlay' },
4949
);
5050

51+
registerStyles(
52+
'vaadin-multi-select-combo-box-container',
53+
css`
54+
:host([all-chips-visible]) {
55+
padding-block: var(--lumo-space-xs);
56+
}
57+
`,
58+
{ moduleId: 'lumo-multi-select-combo-box-container' },
59+
);
60+
5161
const multiSelectComboBox = css`
5262
:host([has-value]) {
5363
padding-inline-start: 0;
@@ -67,10 +77,18 @@ const multiSelectComboBox = css`
6777
mask-image: none;
6878
}
6979
80+
:host([all-chips-visible]) ::slotted([slot='chip']) {
81+
margin-block: calc(var(--lumo-space-xs) / 2);
82+
}
83+
7084
::slotted([slot='chip']:not([readonly]):not([disabled])) {
7185
padding-inline-end: 0;
7286
}
7387
88+
:host([all-chips-visible]) ::slotted([slot='input']) {
89+
min-height: calc(var(--lumo-text-field-size, var(--lumo-size-m)) - 2 * var(--lumo-space-xs));
90+
}
91+
7492
::slotted([slot='chip']:not(:last-of-type)),
7593
::slotted([slot='overflow']:not(:last-of-type)) {
7694
margin-inline-end: var(--lumo-space-xs);

packages/multi-select-combo-box/theme/material/vaadin-multi-select-combo-box-styles.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,11 @@ const multiSelectComboBox = css`
5656
padding: 0 0.5rem;
5757
}
5858
59+
:host([all-chips-visible]) ::slotted([slot='chip']) {
60+
margin-top: 0.25rem;
61+
align-self: flex-start;
62+
}
63+
5964
::slotted([slot='chip']:not([readonly]):not([disabled])) {
6065
padding-inline-end: 0;
6166
}

0 commit comments

Comments
 (0)