From 76affb1e07a288b2f02eda7bc2656d06291e53e0 Mon Sep 17 00:00:00 2001 From: Jeri Peier Date: Tue, 9 Jan 2024 10:13:25 +0100 Subject: [PATCH] fix(sbb-autocomplete): highlight option when options change (#2317) --- src/components/autocomplete/autocomplete.ts | 10 +- src/components/option/optgroup/optgroup.ts | 23 +++- src/components/option/option/option.e2e.ts | 126 ++++++++++++++++++++ 3 files changed, 155 insertions(+), 4 deletions(-) diff --git a/src/components/autocomplete/autocomplete.ts b/src/components/autocomplete/autocomplete.ts index 75917e7d68..76c85883c1 100644 --- a/src/components/autocomplete/autocomplete.ts +++ b/src/components/autocomplete/autocomplete.ts @@ -3,6 +3,7 @@ import { customElement, property, state } from 'lit/decorators.js'; import { ref } from 'lit/directives/ref.js'; import { assignId, getNextElementIndex } from '../core/a11y'; +import { SlotChildObserver } from '../core/common-behaviors'; import { setAttribute, getDocumentWritingMode, @@ -37,7 +38,7 @@ let nextId = 0; * @event {CustomEvent} didClose - Emits whenever the `sbb-autocomplete` is closed. */ @customElement('sbb-autocomplete') -export class SbbAutocompleteElement extends LitElement { +export class SbbAutocompleteElement extends SlotChildObserver(LitElement) { public static override styles: CSSResultGroup = style; public static readonly events = { willOpen: 'willOpen', @@ -241,11 +242,16 @@ export class SbbAutocompleteElement extends LitElement { } } - protected override firstUpdated(): void { + protected override firstUpdated(changedProperties: PropertyValues): void { + super.firstUpdated(changedProperties); this._componentSetup(); this._didLoad = true; } + public override checkChildren(): void { + this._highlightOptions(this.triggerElement?.value); + } + private _syncNegative(): void { this.querySelectorAll?.('sbb-divider').forEach((divider) => (divider.negative = this.negative)); diff --git a/src/components/option/optgroup/optgroup.ts b/src/components/option/optgroup/optgroup.ts index 9c92669f00..85265a1249 100644 --- a/src/components/option/optgroup/optgroup.ts +++ b/src/components/option/optgroup/optgroup.ts @@ -1,6 +1,7 @@ import { CSSResultGroup, html, LitElement, TemplateResult, PropertyValues } from 'lit'; import { customElement, property, state } from 'lit/decorators.js'; +import { SlotChildObserver } from '../../core/common-behaviors'; import { isSafari, isValidAttribute, toggleDatasetEntry, setAttribute } from '../../core/dom'; import { AgnosticMutationObserver } from '../../core/observers'; import type { SbbOptionElement, SbbOptionVariant } from '../option'; @@ -14,7 +15,7 @@ import '../../divider'; * @slot - Use the unnamed slot to add `sbb-option` elements to the `sbb-optgroup`. */ @customElement('sbb-optgroup') -export class SbbOptGroupElement extends LitElement { +export class SbbOptGroupElement extends SlotChildObserver(LitElement) { public static override styles: CSSResultGroup = style; /** Option group label. */ @@ -83,6 +84,12 @@ export class SbbOptGroupElement extends LitElement { } } + protected override checkChildren(): void { + this._proxyDisabledToOptions(); + this._proxyGroupLabelToOptions(); + this._highlightOptions(); + } + private _proxyGroupLabelToOptions(): void { if (!this._inertAriaGroups) { return; @@ -97,6 +104,18 @@ export class SbbOptGroupElement extends LitElement { } } + private _highlightOptions(): void { + const autocomplete = this.closest('sbb-autocomplete'); + if (!autocomplete) { + return; + } + const value = autocomplete.triggerElement?.value; + if (!value) { + return; + } + this._options.forEach((opt) => opt.highlight(value)); + } + private _onNegativeChange(): void { this._negative = isValidAttribute(this, 'data-negative'); } @@ -116,7 +135,7 @@ export class SbbOptGroupElement extends LitElement {
${this.label} - + `; } } diff --git a/src/components/option/option/option.e2e.ts b/src/components/option/option/option.e2e.ts index 2066f1b2f6..bbb0eab504 100644 --- a/src/components/option/option/option.e2e.ts +++ b/src/components/option/option/option.e2e.ts @@ -6,6 +6,7 @@ import '../../autocomplete'; import { waitForLitRender, EventSpy } from '../../core/testing'; import type { SbbFormFieldElement } from '../../form-field'; import '../../form-field'; +import '../optgroup'; import { SbbOptionElement } from './option'; @@ -75,5 +76,130 @@ describe('sbb-option', () => { `); }); + + it('highlight after option label changed', async () => { + const input = element.querySelector('input'); + const autocomplete = element.querySelector('sbb-autocomplete'); + const options = element.querySelectorAll('sbb-option'); + const optionOneLabel = options[0].shadowRoot.querySelector('.sbb-option__label'); + + input.focus(); + await sendKeys({ type: 'Opt' }); + await waitForLitRender(autocomplete); + + expect(optionOneLabel).dom.to.be.equal(` + + + + Opt + ion 1 + + `); + + options[0].textContent = 'Other content'; + await waitForLitRender(autocomplete); + + expect(optionOneLabel).dom.to.be.equal(` + + + Other content + + `); + + options[0].textContent = 'Option'; + await waitForLitRender(autocomplete); + + expect(optionOneLabel).dom.to.be.equal(` + + + + Opt + ion + + `); + }); + + it('highlight later added options', async () => { + const input = element.querySelector('input'); + const autocomplete = element.querySelector('sbb-autocomplete'); + const options = element.querySelectorAll('sbb-option'); + const optionOneLabel = options[0].shadowRoot.querySelector('.sbb-option__label'); + + input.focus(); + await sendKeys({ type: 'Opt' }); + await waitForLitRender(autocomplete); + + expect(optionOneLabel).dom.to.be.equal(` + + + + Opt + ion 1 + + `); + + const newOption = document.createElement('sbb-option'); + newOption.innerText = 'Option 4'; + autocomplete.append(newOption); + await waitForLitRender(autocomplete); + + const newOptionLabel = newOption.shadowRoot.querySelector('.sbb-option__label'); + + expect(newOptionLabel).dom.to.be.equal(` + + + + Opt + ion 4 + + `); + }); + + it('highlight later added options in sbb-optgroup', async () => { + element = await fixture(html` + + + + + Option 1 + + + + `); + + const input = element.querySelector('input'); + const optgroup = element.querySelector('sbb-optgroup'); + const options = element.querySelectorAll('sbb-option'); + const optionOneLabel = options[0].shadowRoot.querySelector('.sbb-option__label'); + + input.focus(); + await sendKeys({ type: 'Opt' }); + await waitForLitRender(element); + + expect(optionOneLabel).dom.to.be.equal(` + + + + Opt + ion 1 + + `); + + const newOption = document.createElement('sbb-option'); + newOption.innerText = 'Option 2'; + optgroup.append(newOption); + await waitForLitRender(element); + + const newOptionLabel = newOption.shadowRoot.querySelector('.sbb-option__label'); + + expect(newOptionLabel).dom.to.be.equal(` + + + + Opt + ion 2 + + `); + }); }); });