diff --git a/packages/select/src/vaadin-lit-select-overlay.js b/packages/select/src/vaadin-lit-select-overlay.js index db02db8c83..5e7402e210 100644 --- a/packages/select/src/vaadin-lit-select-overlay.js +++ b/packages/select/src/vaadin-lit-select-overlay.js @@ -5,24 +5,20 @@ */ import { css, html, LitElement } from 'lit'; import { defineCustomElement } from '@vaadin/component-base/src/define.js'; -import { DirMixin } from '@vaadin/component-base/src/dir-mixin.js'; import { PolylitMixin } from '@vaadin/component-base/src/polylit-mixin.js'; -import { OverlayMixin } from '@vaadin/overlay/src/vaadin-overlay-mixin.js'; -import { PositionMixin } from '@vaadin/overlay/src/vaadin-overlay-position-mixin.js'; import { overlayStyles } from '@vaadin/overlay/src/vaadin-overlay-styles.js'; import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js'; +import { SelectOverlayMixin } from './vaadin-select-overlay-mixin.js'; /** * An element used internally by ``. Not intended to be used separately. * * @extends HTMLElement - * @mixes PositionMixin - * @mixes OverlayMixin - * @mixes DirMixin + * @mixes SelectOverlayMixin * @mixes ThemableMixin * @protected */ -class SelectOverlay extends PositionMixin(OverlayMixin(ThemableMixin(DirMixin(PolylitMixin(LitElement))))) { +class SelectOverlay extends SelectOverlayMixin(ThemableMixin(PolylitMixin(LitElement))) { static get is() { return 'vaadin-select-overlay'; } @@ -36,6 +32,10 @@ class SelectOverlay extends PositionMixin(OverlayMixin(ThemableMixin(DirMixin(Po justify-content: flex-start; } + :host(:not([phone])) [part='overlay'] { + min-width: var(--vaadin-select-overlay-width, var(--vaadin-select-text-field-width)); + } + @media (forced-colors: active) { [part='overlay'] { outline: 3px solid; @@ -75,11 +75,6 @@ class SelectOverlay extends PositionMixin(OverlayMixin(ThemableMixin(DirMixin(Po this.owner._assignMenuElement(menuElement); } } - - /** @protected */ - _getMenuElement() { - return Array.from(this.children).find((el) => el.localName !== 'style'); - } } defineCustomElement(SelectOverlay); diff --git a/packages/select/src/vaadin-select-overlay-mixin.js b/packages/select/src/vaadin-select-overlay-mixin.js new file mode 100644 index 0000000000..7e4716807c --- /dev/null +++ b/packages/select/src/vaadin-select-overlay-mixin.js @@ -0,0 +1,40 @@ +/** + * @license + * Copyright (c) 2017 - 2024 Vaadin Ltd. + * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/ + */ +import { DirMixin } from '@vaadin/component-base/src/dir-mixin.js'; +import { OverlayMixin } from '@vaadin/overlay/src/vaadin-overlay-mixin.js'; +import { PositionMixin } from '@vaadin/overlay/src/vaadin-overlay-position-mixin.js'; + +/** + * @polymerMixin + * @mixes DirMixin + * @mixes OverlayMixin + * @mixes PositionMixin + */ +export const SelectOverlayMixin = (superClass) => + class SelectOverlayMixin extends PositionMixin(OverlayMixin(DirMixin(superClass))) { + static get observers() { + return ['_updateOverlayWidth(opened, owner)']; + } + + /** @protected */ + _getMenuElement() { + return Array.from(this.children).find((el) => el.localName !== 'style'); + } + + /** @private */ + _updateOverlayWidth(opened, owner) { + if (opened && owner) { + const widthProperty = '--vaadin-select-overlay-width'; + const customWidth = getComputedStyle(owner).getPropertyValue(widthProperty); + + if (customWidth === '') { + this.style.removeProperty(widthProperty); + } else { + this.style.setProperty(widthProperty, customWidth); + } + } + } + }; diff --git a/packages/select/src/vaadin-select-overlay.js b/packages/select/src/vaadin-select-overlay.js index 544fafc919..731f3a6d2a 100644 --- a/packages/select/src/vaadin-select-overlay.js +++ b/packages/select/src/vaadin-select-overlay.js @@ -5,11 +5,9 @@ */ import { html, PolymerElement } from '@polymer/polymer/polymer-element.js'; import { defineCustomElement } from '@vaadin/component-base/src/define.js'; -import { DirMixin } from '@vaadin/component-base/src/dir-mixin.js'; -import { OverlayMixin } from '@vaadin/overlay/src/vaadin-overlay-mixin.js'; -import { PositionMixin } from '@vaadin/overlay/src/vaadin-overlay-position-mixin.js'; import { overlayStyles } from '@vaadin/overlay/src/vaadin-overlay-styles.js'; import { css, registerStyles, ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js'; +import { SelectOverlayMixin } from './vaadin-select-overlay-mixin.js'; const selectOverlayStyles = css` :host { @@ -17,6 +15,10 @@ const selectOverlayStyles = css` justify-content: flex-start; } + :host(:not([phone])) [part='overlay'] { + min-width: var(--vaadin-select-overlay-width, var(--vaadin-select-text-field-width)); + } + @media (forced-colors: active) { [part='overlay'] { outline: 3px solid; @@ -33,13 +35,11 @@ registerStyles('vaadin-select-overlay', [overlayStyles, selectOverlayStyles], { * * @customElement * @extends HTMLElement - * @mixes DirMixin - * @mixes OverlayMixin - * @mixes PositionMixin + * @mixes SelectOverlayMixin * @mixes ThemableMixin * @private */ -export class SelectOverlay extends PositionMixin(OverlayMixin(DirMixin(ThemableMixin(PolymerElement)))) { +export class SelectOverlay extends SelectOverlayMixin(ThemableMixin(PolymerElement)) { static get is() { return 'vaadin-select-overlay'; } @@ -75,11 +75,6 @@ export class SelectOverlay extends PositionMixin(OverlayMixin(DirMixin(ThemableM this.owner._assignMenuElement(menuElement); } } - - /** @protected */ - _getMenuElement() { - return Array.from(this.children).find((el) => el.localName !== 'style'); - } } defineCustomElement(SelectOverlay); diff --git a/packages/select/src/vaadin-select.js b/packages/select/src/vaadin-select.js index 352ed54327..9f5dada1a0 100644 --- a/packages/select/src/vaadin-select.js +++ b/packages/select/src/vaadin-select.js @@ -86,6 +86,7 @@ registerStyles('vaadin-select', [fieldShared, inputFieldContainer, screenReaderO * -----------------------------------|------------------------------|---------------------------------- * `--vaadin-field-default-width` | Default width of the field | :host | `12em` * `--vaadin-select-text-field-width` | Effective width of the field | `vaadin-select-overlay` | + * `--vaadin-select-overlay-width` | Width of the overlay | `vaadin-select-overlay` | * * `` provides mostly the same set of shadow DOM parts and state attributes as ``. * See [``](#/elements/vaadin-text-field) for the styling documentation. diff --git a/packages/select/test/select.common.js b/packages/select/test/select.common.js index 0db29d4248..f1f3e83abe 100644 --- a/packages/select/test/select.common.js +++ b/packages/select/test/select.common.js @@ -378,16 +378,6 @@ describe('vaadin-select', () => { expect(overlayRect.top).to.be.equal(inputRect.top); expect(inputRect.right).to.be.equal(inputRect.right); }); - - it('should store the text-field width in the custom CSS property on overlay opening', async () => { - valueButton.style.width = '200px'; - select.opened = true; - await oneEvent(overlay, 'vaadin-overlay-open'); - const prop = '--vaadin-select-text-field-width'; - const inputRect = select._inputContainer.getBoundingClientRect(); - const value = getComputedStyle(overlay).getPropertyValue(prop); - expect(value).to.be.equal(`${inputRect.width}px`); - }); }); describe('overlay opened', () => { @@ -682,6 +672,56 @@ describe('vaadin-select', () => { expect(overlayRect.top).to.be.equal(inputRect.bottom); }); }); + + describe('overlay width', () => { + let overlay; + + beforeEach(() => { + overlay = select._overlayElement; + select.style.setProperty('--vaadin-select-overlay-width', '400px'); + }); + + it('should forward overlay width custom CSS property to the overlay when opened', async () => { + select.opened = true; + await oneEvent(overlay, 'vaadin-overlay-open'); + const prop = '--vaadin-select-overlay-width'; + const value = getComputedStyle(overlay).getPropertyValue(prop); + expect(value).to.be.equal('400px'); + }); + + it('should set overlay part width based on the overlay width custom CSS property', async () => { + select.opened = true; + await oneEvent(overlay, 'vaadin-overlay-open'); + expect(overlay.$.overlay.getBoundingClientRect().width).to.equal(400); + }); + + it('should not set overlay part width based on the custom CSS property when phone', async () => { + select._phone = true; + select.opened = true; + await oneEvent(overlay, 'vaadin-overlay-open'); + expect(overlay.$.overlay.getBoundingClientRect().width).to.not.equal(400); + }); + + it('should store the select width in the custom CSS property on overlay opening', async () => { + select.style.width = '200px'; + select.opened = true; + await oneEvent(overlay, 'vaadin-overlay-open'); + const prop = '--vaadin-select-text-field-width'; + const inputRect = select._inputContainer.getBoundingClientRect(); + const value = getComputedStyle(overlay).getPropertyValue(prop); + expect(value).to.be.equal(`${inputRect.width}px`); + }); + + it('should fallback to the select field width when custom CSS property is unset', async () => { + select.style.width = '200px'; + select.style.removeProperty('--vaadin-select-overlay-width'); + + select.opened = true; + await oneEvent(overlay, 'vaadin-overlay-open'); + + expect(overlay.$.overlay.getBoundingClientRect().width).to.equal(200); + }); + }); }); describe('with value', () => { diff --git a/packages/select/theme/lumo/vaadin-select-styles.js b/packages/select/theme/lumo/vaadin-select-styles.js index 3b039f7191..a0e445086b 100644 --- a/packages/select/theme/lumo/vaadin-select-styles.js +++ b/packages/select/theme/lumo/vaadin-select-styles.js @@ -94,10 +94,6 @@ const selectOverlay = css` --_lumo-item-selected-icon-display: block; } - [part~='overlay'] { - min-width: var(--vaadin-select-text-field-width); - } - /* Small viewport adjustment */ :host([phone]) { /* stylelint-disable declaration-block-no-redundant-longhand-properties */ diff --git a/packages/select/theme/material/vaadin-select-styles.js b/packages/select/theme/material/vaadin-select-styles.js index 355590b813..455658c890 100644 --- a/packages/select/theme/material/vaadin-select-styles.js +++ b/packages/select/theme/material/vaadin-select-styles.js @@ -75,10 +75,4 @@ registerStyles( { moduleId: 'material-select-value-button' }, ); -const selectOverlay = css` - [part='overlay'] { - min-width: var(--vaadin-select-text-field-width); - } -`; - -registerStyles('vaadin-select-overlay', [menuOverlay, selectOverlay], { moduleId: 'material-select-overlay' }); +registerStyles('vaadin-select-overlay', [menuOverlay], { moduleId: 'material-select-overlay' });