From fbc3739a6c79cea6cd5e585ff06fdc77bdd78a5d Mon Sep 17 00:00:00 2001 From: Jonathan Meyer <26874831+atmgrifter00@users.noreply.github.com> Date: Mon, 8 Apr 2024 10:53:50 -0500 Subject: [PATCH 01/18] Working implementation. --- .../nimble-components/src/select/index.ts | 26 ++++++++++++++++++- .../nimble-components/src/select/styles.ts | 4 +++ .../src/select/tests/select.stories.ts | 5 +++- 3 files changed, 33 insertions(+), 2 deletions(-) diff --git a/packages/nimble-components/src/select/index.ts b/packages/nimble-components/src/select/index.ts index 5cd9a29f4b..0891536f98 100644 --- a/packages/nimble-components/src/select/index.ts +++ b/packages/nimble-components/src/select/index.ts @@ -4,7 +4,8 @@ import { html, observable, Observable, - volatile + volatile, + when } from '@microsoft/fast-element'; import { AnchoredRegion, @@ -42,6 +43,7 @@ import { ListOption } from '../list-option'; import { FilterMode } from './types'; import { diacriticInsensitiveStringNormalizer } from '../utilities/models/string-normalizers'; import { FormAssociatedSelect } from './models/select-form-associated'; +import { iconCircleXTag } from '../icons/circle-x'; declare global { interface HTMLElementTagNameMap { @@ -90,6 +92,9 @@ export class Select @attr({ attribute: 'filter-mode' }) public filterMode: FilterMode = FilterMode.none; + @attr({ attribute: 'clearable', mode: 'boolean' }) + public clearable = false; + /** * @internal */ @@ -193,6 +198,7 @@ export class Select private _value = ''; private forcedPosition = false; private indexWhenOpened?: number; + private placeholderOption?: ListboxOption; /** * @internal @@ -301,6 +307,7 @@ export class Select notifier.subscribe(this, 'hidden'); notifier.subscribe(this, 'disabled'); }); + this.findAndCachePlaceholderOption(); this.setProxyOptions(); this.updateValue(); // We need to force an update to the filteredOptions observable @@ -437,6 +444,16 @@ export class Select ); } + public clearClickHandler(e: MouseEvent): void { + if (this.placeholderOption) { + this.selectedIndex = this.options.indexOf(this.placeholderOption); + this.committedSelectedOption = this.placeholderOption; + } else { + this.selectedIndex = -1; + } + e.stopPropagation(); + } + /** * @internal */ @@ -940,6 +957,10 @@ export class Select }); } + private findAndCachePlaceholderOption(): void { + this.placeholderOption = this.options.find(o => o.selected && o.disabled && o.hidden); + } + private filterChanged(): void { this.filterOptions(); } @@ -985,6 +1006,9 @@ const nimbleSelect = Select.compose({ class="error-icon" > ${errorTextTemplate} + ${when(x => x.clearable && !x.displayPlaceholder && x.selectedIndex >= 0, html` - <${iconCircleXTag} class="clear-icon" @click="${(x, c) => x.clearClickHandler(c.event as MouseEvent)}"> - `)} ` }); diff --git a/packages/nimble-components/src/select/styles.ts b/packages/nimble-components/src/select/styles.ts index a9f34ad852..c043369bc1 100644 --- a/packages/nimble-components/src/select/styles.ts +++ b/packages/nimble-components/src/select/styles.ts @@ -38,8 +38,11 @@ export const styles = css` order: 4; } - .clear-icon { + .clear-button { order: 3; + width: auto; + height: auto; + margin-left: ${smallPadding}; } .error-icon { diff --git a/packages/nimble-components/src/select/template.ts b/packages/nimble-components/src/select/template.ts index 3266934432..703b321cd9 100644 --- a/packages/nimble-components/src/select/template.ts +++ b/packages/nimble-components/src/select/template.ts @@ -22,6 +22,8 @@ import { filterSearchLabel } from '../label-provider/core/label-tokens'; import { FilterMode } from './types'; +import { buttonTag } from '../button'; +import { iconXmarkTag } from '../icons/xmark'; /* eslint-disable @typescript-eslint/indent */ // prettier-ignore @@ -65,6 +67,15 @@ SelectOptions
(x.hasOverflow && x.displayValue ? x.displayValue : null)}> ${x => x.displayValue}
+ ${when(x => x.clearable && !x.displayPlaceholder && x.selectedIndex >= 0, html` <${buttonTag} class="clear-button" + part="clear-button" content-hidden appearance="ghost" @click="${(x, c) => x.clearClickHandler(c.event as MouseEvent)}"> diff --git a/packages/nimble-components/src/select/testing/select.pageobject.ts b/packages/nimble-components/src/select/testing/select.pageobject.ts index d3f0faa1b9..10e8c8faba 100644 --- a/packages/nimble-components/src/select/testing/select.pageobject.ts +++ b/packages/nimble-components/src/select/testing/select.pageobject.ts @@ -130,6 +130,21 @@ export class SelectPageObject { } public clickClearButton(): void { + if (!this.selectElement.clearable) { + throw new Error( + 'Select must set "clearable" in order to click clear button' + ); + } + + if ( + this.selectElement.selectedIndex === -1 + || this.selectElement.displayPlaceholder + ) { + throw new Error( + 'Select must have a selected element in order to click clear button' + ); + } + const clearButton = this.getClearButton(); clearButton?.click(); } @@ -270,21 +285,6 @@ export class SelectPageObject { } private getClearButton(): Button | null | undefined { - if (!this.selectElement.clearable) { - throw new Error( - 'Select must set "clearable" in order to click clear button' - ); - } - - if ( - this.selectElement.selectedIndex === -1 - || this.selectElement.displayPlaceholder - ) { - throw new Error( - 'Select must have a selected element in order to click clear button' - ); - } - return this.selectElement.shadowRoot?.querySelector