Skip to content

experiment: add multi-select-combo-box base styles and visual tests #9608

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 18 commits into from
Jul 4, 2025
Merged
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
2 changes: 2 additions & 0 deletions packages/multi-select-combo-box/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
"lit.d.ts",
"lit.js",
"src",
"!src/styles/*-base-styles.d.ts",
"!src/styles/*-base-styles.js",
"theme",
"vaadin-*.d.ts",
"vaadin-*.js",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/**
* @license
* Copyright (c) 2021 - 2025 Vaadin Ltd.
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
*/
import type { CSSResult } from 'lit';

export const multiSelectComboBoxStyles: CSSResult;
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/**
* @license
* Copyright (c) 2021 - 2025 Vaadin Ltd.
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
*/
import { css } from 'lit';
import { comboBoxStyles } from '@vaadin/combo-box/src/styles/vaadin-combo-box-base-styles.js';

export const multiSelectComboBoxStyles = [
comboBoxStyles,
css`
@layer base {
:host {
max-width: 100%;
--_input-min-width: var(--vaadin-multi-select-combo-box-input-min-width, 4rem);
--_chip-min-width: var(--vaadin-multi-select-combo-box-chip-min-width, var(--vaadin-chip-min-width, 48px));
--_wrapper-gap: var(--vaadin-multi-select-combo-box-chips-gap, 2px);
}

#chips {
display: flex;
align-items: center;
gap: var(--vaadin-multi-select-combo-box-chips-gap, 2px);
}

::slotted(input) {
box-sizing: border-box;
flex: 1 0 var(--_input-min-width);
}

::slotted([slot='chip']),
::slotted([slot='overflow']) {
flex: 0 1 auto;
}

::slotted([slot='chip']) {
overflow: hidden;
}

:host(:is([readonly], [disabled])) ::slotted(input) {
flex-grow: 0;
flex-basis: 0;
padding: 0;
}

:host([readonly]:not([disabled])) [part$='button'] {
cursor: var(--vaadin-clickable-cursor);
}

:host([auto-expand-vertically]) #chips {
display: contents;
}

:host([auto-expand-horizontally]) [class$='container'] {
width: auto;
}
}
`,
];
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
/**
* @license
* Copyright (c) 2021 - 2025 Vaadin Ltd.
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
*/
import '@vaadin/component-base/src/style-props.js';
import { css } from 'lit';

export const multiSelectComboBoxChipStyles = css`
@layer base {
:host {
display: inline-flex;
align-items: center;
justify-content: center;
white-space: nowrap;
box-sizing: border-box;
gap: var(--vaadin-chip-gap, var(--vaadin-chip-padding, 0.3em));
background: var(--vaadin-chip-background, var(--vaadin-background-container));
color: var(--vaadin-chip-color, var(--vaadin-color));
font-size: var(--vaadin-chip-font-size, 0.875em);
font-weight: var(--vaadin-chip-font-weight, 500);
line-height: var(--vaadin-input-field-value-line-height, inherit);
padding: 0 var(--vaadin-chip-padding, 0.3em);
height: var(--vaadin-chip-height, calc(1lh / 0.875));
border-radius: var(--vaadin-chip-border-radius, var(--vaadin-radius-m));
border: var(--vaadin-chip-border-width, 1px) solid var(--vaadin-chip-border-color, var(--vaadin-border-color));
cursor: default;
}

:host(:not([slot='overflow'])) {
min-width: min(
max-content,
var(--vaadin-multi-select-combo-box-chip-min-width, var(--vaadin-chip-min-width, 48px))
);
}

:host([focused]) {
outline: var(--vaadin-focus-ring-width) solid var(--vaadin-focus-ring-color);
outline-offset: calc(var(--vaadin-chip-border-width, 1px) * -1);
}

[part='label'] {
overflow: hidden;
text-overflow: ellipsis;
}

[part='remove-button'] {
flex: none;
display: block;
margin-inline: auto calc(var(--vaadin-chip-padding, 0.3em) * -1);
color: var(--vaadin-chip-remove-button-color, var(--vaadin-color-subtle));
cursor: var(--vaadin-clickable-cursor);
}

[part='remove-button']::before {
content: '';
display: block;
width: var(--vaadin-icon-size, 1lh);
height: var(--vaadin-icon-size, 1lh);
background: currentColor;
mask-image: var(--_vaadin-icon-cross);
}

:host([disabled]) {
cursor: var(--vaadin-disabled-cursor);
}

:host([disabled]) [part='label'] {
--vaadin-chip-color: var(--vaadin-color-disabled);
}

:host([hidden]),
:host(:is([readonly], [disabled], [slot='overflow'])) [part='remove-button'] {
display: none !important;
}

:host([slot='overflow']) {
position: relative;
margin-inline-start: 8px;
min-width: 1.5em;
}

:host([slot='overflow'])::before,
:host([slot='overflow'])::after {
content: '';
position: absolute;
inset: calc(var(--vaadin-chip-border-width, 1px) * -1);
border-inline-start: 2px solid var(--vaadin-chip-border-color, var(--vaadin-border-color));
border-radius: inherit;
}

:host([slot='overflow'])::before {
left: calc(-4px - var(--vaadin-chip-border-width, 1px));
}

:host([slot='overflow'])::after {
left: calc(-8px - var(--vaadin-chip-border-width, 1px));
}

:host([count='2']) {
margin-inline-start: 4px;
}

:host([count='1']) {
margin-inline-start: 0;
}

:host([count='2'])::after,
:host([count='1'])::before,
:host([count='1'])::after {
display: none;
}
}
`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/**
* @license
* Copyright (c) 2021 - 2025 Vaadin Ltd.
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
*/
import { css } from 'lit';
import { comboBoxOverlayStyles } from '@vaadin/combo-box/src/styles/vaadin-combo-box-overlay-base-styles.js';

export const multiSelectComboBoxOverlayStyles = [
comboBoxOverlayStyles,
css`
@layer base {
#overlay {
width: var(
--vaadin-multi-select-combo-box-overlay-width,
var(--_vaadin-multi-select-combo-box-overlay-default-width, auto)
);
}
}
`,
];
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/**
* @license
* Copyright (c) 2021 - 2025 Vaadin Ltd.
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
*/
import { comboBoxScrollerStyles } from '@vaadin/combo-box/src/styles/vaadin-combo-box-scroller-base-styles.js';

export const multiSelectComboBoxScrollerStyles = comboBoxScrollerStyles;
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ class MultiSelectComboBoxContainer extends InputContainer {
display: flex;
width: 100%;
min-width: 0;
gap: var(--_wrapper-gap);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added reset for this custom CSS property to Lumo as otherwise it breaks visual tests.
Moving this CSS to styles folder is also an option but that sounds like an overkill.

}

:host([auto-expand-vertically]) #wrapper {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,6 @@ export interface MultiSelectComboBoxEventMap<TItem> extends HTMLElementEventMap
* `--vaadin-field-default-width` | Default width of the field | `12em`
* `--vaadin-multi-select-combo-box-overlay-width` | Width of the overlay | `auto`
* `--vaadin-multi-select-combo-box-overlay-max-height` | Max height of the overlay | `65vh`
* `--vaadin-multi-select-combo-box-chip-min-width` | Min width of the chip | `50px`
* `--vaadin-multi-select-combo-box-input-min-width` | Min width of the input | `4em`
*
* ### Internal components
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,6 @@ import { MultiSelectComboBoxMixin } from './vaadin-multi-select-combo-box-mixin.
* `--vaadin-field-default-width` | Default width of the field | `12em`
* `--vaadin-multi-select-combo-box-overlay-width` | Width of the overlay | `auto`
* `--vaadin-multi-select-combo-box-overlay-max-height` | Max height of the overlay | `65vh`
* `--vaadin-multi-select-combo-box-chip-min-width` | Min width of the chip | `50px`
Copy link
Member

@web-padawan web-padawan Jul 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is now only used by Lumo so I think we should either remove it from API docs (as it has no effect on base styles) or make a separate PR for renaming it also in core / Lumo styles. Not sure which is better.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suppose we could also keep it working in base styles, but remove it from the docs or mark it deprecated.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I restored it for now to avoid unintentional breaking change but IMO it's ok to remove it from API docs.

* `--vaadin-multi-select-combo-box-input-min-width` | Min width of the input | `4em`
*
* ### Internal components
Expand Down
4 changes: 2 additions & 2 deletions packages/multi-select-combo-box/test/chips.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ describe('chips', () => {
it('should update overflow chip on resize when width changes', async () => {
expect(overflow.hasAttribute('hidden')).to.be.false;

comboBox.style.width = '350px';
comboBox.style.width = '400px';
await nextResize(comboBox);
expect(overflow.hasAttribute('hidden')).to.be.true;

Expand All @@ -181,7 +181,7 @@ describe('chips', () => {
});

it('should update overflow chip on clear button state change', async () => {
comboBox.style.width = '340px';
comboBox.style.width = '370px';
await nextResize(comboBox);

comboBox.clearButtonVisible = true;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,38 +1,34 @@
import { css, registerStyles } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';

// TODO: subset of Lumo needed for unit tests to pass.
// These should be eventually covered by base styles.
// TODO: subset of base styles needed for unit tests to pass.
// Should be eventually removed after switching to use base styles.
registerStyles(
'vaadin-multi-select-combo-box',
css`
::slotted([slot='chip']),
::slotted([slot='overflow']) {
padding: 0.3125em 0.5625em;
}

::slotted([slot='chip']:not(:last-of-type)),
::slotted([slot='overflow']:not(:last-of-type)) {
margin-inline-end: 0.25rem;
:host {
--_chip-min-width: 48px;
--_wrapper-gap: 2px;
}

::slotted([slot='chip']:not([readonly]):not([disabled])) {
padding-inline-end: 0;
#chips {
gap: 2px;
}

::slotted(input) {
padding: 0 0.25em;
::slotted([slot='chip']),
::slotted([slot='overflow']) {
padding: 0 0.3em;
}

[part$='button'] {
flex: none;
font-size: 1.5em;
width: 1em;
height: 1em;
line-height: 1.25;
}

[part$='button']::before {
display: block;
height: 100%;
content: '';
height: 1lh;
width: 1lh;
}
`,
);
Expand All @@ -41,7 +37,8 @@ registerStyles(
'vaadin-multi-select-combo-box-container',
css`
:host {
padding: 0 6px;
padding: 6px 8px;
gap: 0.5em;
}
`,
);
Expand All @@ -51,7 +48,20 @@ registerStyles(
css`
:host {
font-family: -apple-system, 'system-ui', Roboto, 'Segoe UI', Helvetica, Arial, sans-serif;
font-size: 0.75rem;
font-size: 0.875rem;
border: solid 1px;
gap: 0.3em;
}

:host([slot='overflow']) {
min-width: 1.5em;
}

:host(:not([slot='overflow'])) {
min-width: min(
max-content,
var(--vaadin-multi-select-combo-box-chip-min-width, var(--vaadin-chip-min-width, 48px))
);
}

[part='label'] {
Expand All @@ -60,9 +70,15 @@ registerStyles(

[part='remove-button'] {
display: flex;
width: 1.25em;
height: 1.25em;
font-size: 1.5em;
flex: 0 0 auto;
line-height: 1.25;
}

[part='remove-button']::before {
content: '';
display: block;
width: 1lh;
height: 1lh;
}
`,
);
Expand Down
Loading