From 635e52e890f46d3818f7f80af9fe313a137a0f3f Mon Sep 17 00:00:00 2001 From: Esteban Gonzalez Date: Wed, 7 Sep 2022 17:38:42 -0700 Subject: [PATCH] refactor(autocomplete): Extend TextField PiperOrigin-RevId: 472856121 --- autocomplete/filled-autocomplete.ts | 11 +++- autocomplete/lib/autocomplete.ts | 83 ++++++----------------------- 2 files changed, 25 insertions(+), 69 deletions(-) diff --git a/autocomplete/filled-autocomplete.ts b/autocomplete/filled-autocomplete.ts index 4d8491c5ee..73c8e5ba7d 100644 --- a/autocomplete/filled-autocomplete.ts +++ b/autocomplete/filled-autocomplete.ts @@ -6,11 +6,15 @@ import '@material/web/list/list.js'; import '@material/web/menu-surface/menu-surface.js'; -import '@material/web/textfield/filled-text-field.js'; +import '@material/web/field/filled-field.js'; import {customElement} from 'lit/decorators.js'; import {literal} from 'lit/static-html.js'; +import {styles as filledForcedColorsStyles} from '../textfield/lib/filled-forced-colors-styles.css.js'; +import {styles as filledStyles} from '../textfield/lib/filled-styles.css.js'; +import {styles as sharedStyles} from '../textfield/lib/shared-styles.css.js'; + import {Autocomplete} from './lib/autocomplete.js'; declare global { @@ -26,7 +30,10 @@ declare global { */ @customElement('md-filled-autocomplete') export class MdFilledAutocomplete extends Autocomplete { + static override styles = + [sharedStyles, filledStyles, filledForcedColorsStyles]; + protected override readonly listTag = literal`md-list`; protected override readonly menuSurfaceTag = literal`md-menu-surface`; - protected override readonly textFieldTag = literal`md-filled-text-field`; + protected override readonly fieldTag = literal`md-filled-field`; } diff --git a/autocomplete/lib/autocomplete.ts b/autocomplete/lib/autocomplete.ts index 6972f6d003..165eea7f5c 100644 --- a/autocomplete/lib/autocomplete.ts +++ b/autocomplete/lib/autocomplete.ts @@ -6,9 +6,7 @@ // TODO(b/243558385): remove compat dependencies import {observer} from '@material/web/compat/base/observer.js'; -import {redispatchEvent} from '@material/web/controller/events.js'; -import {stringConverter} from '@material/web/controller/string-converter.js'; -import {html, LitElement, PropertyValues, TemplateResult} from 'lit'; +import {html, PropertyValues, TemplateResult} from 'lit'; import {property, query, queryAssignedElements, state} from 'lit/decorators.js'; import {html as staticHtml, StaticValue} from 'lit/static-html.js'; @@ -19,26 +17,12 @@ import {TextField} from '../../textfield/lib/text-field.js'; import {AutocompleteItem} from './autocompleteitem/autocomplete-item.js'; /** @soyCompatible */ -export abstract class Autocomplete extends LitElement { +export abstract class Autocomplete extends TextField { static override shadowRootOptions: ShadowRootInit = {mode: 'open', delegatesFocus: true}; - // TextField properties - // TODO(b/243143708): Add all the remaining text field properties - @property({type: Boolean, reflect: true}) disabled = false; - @property({type: Boolean, reflect: true}) error = false; - @property({type: String}) errorText = ''; - @property({type: String}) label?: string; - @property({type: Boolean, reflect: true}) required = false; - @property({type: String}) value = ''; - @property({type: String}) prefixText = ''; - @property({type: String}) suffixText = ''; - @property({type: Boolean}) hasLeadingIcon = false; - @property({type: Boolean}) hasTrailingIcon = false; - @property({type: String}) supportingText = ''; - @property({type: String}) supportingTextId = 'support'; - @property({type: String, reflect: true, converter: stringConverter}) - placeholder = ''; + override readonly role = 'combobox'; + override readonly ariaAutoComplete = 'list'; /** * The ID on the list element, used for SSR. @@ -49,11 +33,9 @@ export abstract class Autocomplete extends LitElement { */ @property({type: String}) itemIdPrefix = 'autocomplete-item'; - protected abstract readonly textFieldTag: StaticValue; protected abstract readonly menuSurfaceTag: StaticValue; protected abstract readonly listTag: StaticValue; - @query('.md3-autocomplete__text-field') textField?: TextField|null; @query('.md3-autocomplete__menu-surface') menuSurface?: MenuSurface|null; @query('.md3-autocomplete__list') list?: List|null; @@ -63,51 +45,26 @@ export abstract class Autocomplete extends LitElement { @state() // tslint:disable-next-line:no-new-decorators @observer(function(this: Autocomplete) { this.updateSelectedItem(); + this.ariaActiveDescendant = this.selectedItem?.itemId ?? null; }) protected selectedItem: AutocompleteItem|null = null; /** @soyTemplate */ override render(): TemplateResult { return html`
- ${this.renderTextField()} + ${super.render()} ${this.renderMenuSurface()}
`; } override firstUpdated(changedProperties: PropertyValues) { super.firstUpdated(changedProperties); - this.menuSurface!.anchor = this.textField!; - } - - /** @soyTemplate */ - protected renderTextField(): TemplateResult { - const activeDescendant = this.selectedItem?.itemId ?? ''; - - return staticHtml`<${this.textFieldTag} - class="md3-autocomplete__text-field" - role="combobox" - aria-autocomplete="list" - aria-activedescendant=${activeDescendant} - aria-controls=${this.listId} - ?disabled=${this.disabled} - ?error=${this.error} - errorText=${this.errorText} - ?hasTrailingIcon=${this.hasTrailingIcon} - ?hasLeadingIcon=${this.hasLeadingIcon} - label=${this.label} - value=${this.value} - prefixText=${this.prefixText} - suffixText=${this.suffixText} - supportingText=${this.supportingText} - supportingTextId=${this.supportingTextId} - ?required=${this.required} - placeholder=${this.placeholder}> - `; + this.menuSurface!.anchor = this; } /** @soyTemplate */ @@ -132,18 +89,16 @@ export abstract class Autocomplete extends LitElement { open() { this.menuSurface?.show(); - if (!this.textField) return; - this.textField.ariaExpanded = 'true'; + this.ariaExpanded = 'true'; } close() { this.menuSurface?.close(); this.selectedItem = null; - if (!this.textField) return; - this.textField.ariaExpanded = 'false'; + this.ariaExpanded = 'false'; } - protected handleClick(event: PointerEvent) { + protected handleClicked(event: PointerEvent) { // When clicking the list (not items nor text field) the menu should stay // open. if (this.isOpen() && @@ -154,13 +109,13 @@ export abstract class Autocomplete extends LitElement { } } - // TODO(b/243389569): Revisit focus control when extending textfield - protected handleFocusout() { + protected override handleFocusout() { if (this.matches(':focus-within')) { - this.textField?.focus(); + this.getInput().focus(); return; } this.close(); + this.focused = false; } protected handleAction(event: CustomEvent<{item: AutocompleteItem}>) { @@ -168,12 +123,6 @@ export abstract class Autocomplete extends LitElement { this.value = detail.item.headline; } - protected handleInput(event: InputEvent) { - if (!event.target) return; - this.value = (event.target as HTMLInputElement).value; - redispatchEvent(this, event); - } - protected handleKeydown(event: KeyboardEvent) { let bubble = true; const altKey = event.altKey; @@ -236,13 +185,13 @@ export abstract class Autocomplete extends LitElement { break; case 'Home': - this.textField?.setSelectionRange(0, 0); + this.setSelectionRange(0, 0); this.selectedItem = null; bubble = false; break; case 'End': - this.textField?.setSelectionRange(this.value.length, this.value.length); + this.setSelectionRange(this.value.length, this.value.length); this.selectedItem = null; bubble = false; break;