From 9f662afa71a72df516d8b0d312fb4e6d0be9a0a7 Mon Sep 17 00:00:00 2001 From: Daniel Smith Date: Thu, 21 Jun 2018 13:03:55 -0400 Subject: [PATCH 1/2] feat(autocomplete): allow panel to have a width value of auto For scenarios when the width of autocomplete panel needs to exceed the width of its input (host) in order to display the full text of each option. Fixes #11773 --- src/lib/autocomplete/autocomplete-trigger.ts | 10 +++++++--- src/lib/autocomplete/autocomplete.spec.ts | 15 +++++++++++++++ src/lib/autocomplete/autocomplete.ts | 10 ++++++++++ 3 files changed, 32 insertions(+), 3 deletions(-) diff --git a/src/lib/autocomplete/autocomplete-trigger.ts b/src/lib/autocomplete/autocomplete-trigger.ts index 7be99c44f356..09ce90020481 100644 --- a/src/lib/autocomplete/autocomplete-trigger.ts +++ b/src/lib/autocomplete/autocomplete-trigger.ts @@ -523,13 +523,13 @@ export class MatAutocompleteTrigger implements ControlValueAccessor, OnDestroy { if (this._viewportRuler) { this._viewportSubscription = this._viewportRuler.change().subscribe(() => { if (this.panelOpen && this._overlayRef) { - this._overlayRef.updateSize({width: this._getHostWidth()}); + this._overlayRef.updateSize({width: this._getWidth()}); } }); } } else { // Update the panel width and direction, in case anything has changed. - this._overlayRef.updateSize({width: this._getHostWidth()}); + this._overlayRef.updateSize({width: this._getWidth()}); } if (this._overlayRef && !this._overlayRef.hasAttached()) { @@ -553,7 +553,7 @@ export class MatAutocompleteTrigger implements ControlValueAccessor, OnDestroy { return new OverlayConfig({ positionStrategy: this._getOverlayPosition(), scrollStrategy: this._scrollStrategy(), - width: this._getHostWidth(), + width: this._getWidth(), direction: this._dir }); } @@ -579,6 +579,10 @@ export class MatAutocompleteTrigger implements ControlValueAccessor, OnDestroy { return this._formField ? this._formField.getConnectedOverlayOrigin() : this._element; } + private _getWidth(): number | string { + return this.autocomplete.panelWidthAuto ? 'auto' : this._getHostWidth(); + } + /** Returns the width of the input element, so the panel width can match it. */ private _getHostWidth(): number { return this._getConnectedElement().nativeElement.getBoundingClientRect().width; diff --git a/src/lib/autocomplete/autocomplete.spec.ts b/src/lib/autocomplete/autocomplete.spec.ts index 7d1240b602f4..4ed582b56609 100644 --- a/src/lib/autocomplete/autocomplete.spec.ts +++ b/src/lib/autocomplete/autocomplete.spec.ts @@ -1922,6 +1922,21 @@ describe('MatAutocomplete', () => { expect(Math.ceil(parseFloat(overlayPane.style.width as string))).toBe(400); })); + it('should have panel width set to auto', () => { + const widthFixture = createComponent(SimpleAutocomplete); + + widthFixture.componentInstance.width = 300; + widthFixture.detectChanges(); + + widthFixture.componentInstance.trigger.autocomplete.panelWidthAuto = true; + widthFixture.componentInstance.trigger.openPanel(); + widthFixture.detectChanges(); + + const overlayPane = overlayContainerElement.querySelector('.cdk-overlay-pane') as HTMLElement; + + expect(overlayPane.style.width).toBe('auto'); + }); + it('should show the panel when the options are initialized later within a component with ' + 'OnPush change detection', fakeAsync(() => { let fixture = createComponent(AutocompleteWithOnPushDelay); diff --git a/src/lib/autocomplete/autocomplete.ts b/src/lib/autocomplete/autocomplete.ts index 38894866c8ef..2ecfbe7bcbcb 100644 --- a/src/lib/autocomplete/autocomplete.ts +++ b/src/lib/autocomplete/autocomplete.ts @@ -126,6 +126,16 @@ export class MatAutocomplete extends _MatAutocompleteMixinBase implements AfterC } private _autoActiveFirstOption: boolean; + /** + * Whether the width of the autocomplete panel is set to 'auto'. When 'false', the width of the + * panel will match the width of the host. + */ + @Input() + get panelWidthAuto(): boolean { return this._panelWidthAuto; } + set panelWidthAuto(value: boolean) { + this._panelWidthAuto = coerceBooleanProperty(value); + } + private _panelWidthAuto: boolean; /** Event that is emitted whenever an option from the list is selected. */ @Output() readonly optionSelected: EventEmitter = From 5049b8c446b37332d0393492822dd5cd997231a0 Mon Sep 17 00:00:00 2001 From: Daniel Smith Date: Tue, 26 Jun 2018 16:03:42 -0400 Subject: [PATCH 2/2] feat(autocomplete): accept truthy value for panel width When value is supplied it will be used as a CSS value for the panel width. Otherwise, width will default to match the host. Fixes #11773 --- src/lib/autocomplete/autocomplete-trigger.ts | 10 ++--- src/lib/autocomplete/autocomplete.spec.ts | 47 ++++++++++++++++---- src/lib/autocomplete/autocomplete.ts | 11 ++--- 3 files changed, 46 insertions(+), 22 deletions(-) diff --git a/src/lib/autocomplete/autocomplete-trigger.ts b/src/lib/autocomplete/autocomplete-trigger.ts index 09ce90020481..9bb71cba4655 100644 --- a/src/lib/autocomplete/autocomplete-trigger.ts +++ b/src/lib/autocomplete/autocomplete-trigger.ts @@ -523,13 +523,13 @@ export class MatAutocompleteTrigger implements ControlValueAccessor, OnDestroy { if (this._viewportRuler) { this._viewportSubscription = this._viewportRuler.change().subscribe(() => { if (this.panelOpen && this._overlayRef) { - this._overlayRef.updateSize({width: this._getWidth()}); + this._overlayRef.updateSize({width: this._getPanelWidth()}); } }); } } else { // Update the panel width and direction, in case anything has changed. - this._overlayRef.updateSize({width: this._getWidth()}); + this._overlayRef.updateSize({width: this._getPanelWidth()}); } if (this._overlayRef && !this._overlayRef.hasAttached()) { @@ -553,7 +553,7 @@ export class MatAutocompleteTrigger implements ControlValueAccessor, OnDestroy { return new OverlayConfig({ positionStrategy: this._getOverlayPosition(), scrollStrategy: this._scrollStrategy(), - width: this._getWidth(), + width: this._getPanelWidth(), direction: this._dir }); } @@ -579,8 +579,8 @@ export class MatAutocompleteTrigger implements ControlValueAccessor, OnDestroy { return this._formField ? this._formField.getConnectedOverlayOrigin() : this._element; } - private _getWidth(): number | string { - return this.autocomplete.panelWidthAuto ? 'auto' : this._getHostWidth(); + private _getPanelWidth(): number | string { + return this.autocomplete.panelWidth || this._getHostWidth(); } /** Returns the width of the input element, so the panel width can match it. */ diff --git a/src/lib/autocomplete/autocomplete.spec.ts b/src/lib/autocomplete/autocomplete.spec.ts index 4ed582b56609..58c9c6d9ea25 100644 --- a/src/lib/autocomplete/autocomplete.spec.ts +++ b/src/lib/autocomplete/autocomplete.spec.ts @@ -1922,19 +1922,48 @@ describe('MatAutocomplete', () => { expect(Math.ceil(parseFloat(overlayPane.style.width as string))).toBe(400); })); - it('should have panel width set to auto', () => { - const widthFixture = createComponent(SimpleAutocomplete); + it('should have panel width match host width by default', () => { + const widthFixture = createComponent(SimpleAutocomplete); + + widthFixture.componentInstance.width = 300; + widthFixture.detectChanges(); + + widthFixture.componentInstance.trigger.openPanel(); + widthFixture.detectChanges(); + + const overlayPane = overlayContainerElement.querySelector('.cdk-overlay-pane') as HTMLElement; + + expect(Math.ceil(parseFloat(overlayPane.style.width as string))).toBe(300); + }); + + it('should have panel width set to string value', () => { + const widthFixture = createComponent(SimpleAutocomplete); + + widthFixture.componentInstance.width = 300; + widthFixture.detectChanges(); + + widthFixture.componentInstance.trigger.autocomplete.panelWidth = 'auto'; + widthFixture.componentInstance.trigger.openPanel(); + widthFixture.detectChanges(); + + const overlayPane = overlayContainerElement.querySelector('.cdk-overlay-pane') as HTMLElement; + + expect(overlayPane.style.width).toBe('auto'); + }); - widthFixture.componentInstance.width = 300; - widthFixture.detectChanges(); + it('should have panel width set to number value', () => { + const widthFixture = createComponent(SimpleAutocomplete); + + widthFixture.componentInstance.width = 300; + widthFixture.detectChanges(); - widthFixture.componentInstance.trigger.autocomplete.panelWidthAuto = true; - widthFixture.componentInstance.trigger.openPanel(); - widthFixture.detectChanges(); + widthFixture.componentInstance.trigger.autocomplete.panelWidth = 400; + widthFixture.componentInstance.trigger.openPanel(); + widthFixture.detectChanges(); - const overlayPane = overlayContainerElement.querySelector('.cdk-overlay-pane') as HTMLElement; + const overlayPane = overlayContainerElement.querySelector('.cdk-overlay-pane') as HTMLElement; - expect(overlayPane.style.width).toBe('auto'); + expect(Math.ceil(parseFloat(overlayPane.style.width as string))).toBe(400); }); it('should show the panel when the options are initialized later within a component with ' + diff --git a/src/lib/autocomplete/autocomplete.ts b/src/lib/autocomplete/autocomplete.ts index 2ecfbe7bcbcb..fb79a51face1 100644 --- a/src/lib/autocomplete/autocomplete.ts +++ b/src/lib/autocomplete/autocomplete.ts @@ -127,15 +127,10 @@ export class MatAutocomplete extends _MatAutocompleteMixinBase implements AfterC private _autoActiveFirstOption: boolean; /** - * Whether the width of the autocomplete panel is set to 'auto'. When 'false', the width of the - * panel will match the width of the host. + * Specify the width of the autocomplete panel. Can be any CSS sizing value, otherwise it will + * match the width of its host. */ - @Input() - get panelWidthAuto(): boolean { return this._panelWidthAuto; } - set panelWidthAuto(value: boolean) { - this._panelWidthAuto = coerceBooleanProperty(value); - } - private _panelWidthAuto: boolean; + @Input() panelWidth: string | number; /** Event that is emitted whenever an option from the list is selected. */ @Output() readonly optionSelected: EventEmitter =