Skip to content

Commit

Permalink
fix(sbb-autocomplete): highlight option when options change (#2317)
Browse files Browse the repository at this point in the history
  • Loading branch information
jeripeierSBB authored Jan 9, 2024
1 parent 37ea7d1 commit 76affb1
Show file tree
Hide file tree
Showing 3 changed files with 155 additions and 4 deletions.
10 changes: 8 additions & 2 deletions src/components/autocomplete/autocomplete.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -37,7 +38,7 @@ let nextId = 0;
* @event {CustomEvent<void>} 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',
Expand Down Expand Up @@ -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));

Expand Down
23 changes: 21 additions & 2 deletions src/components/option/optgroup/optgroup.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -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. */
Expand Down Expand Up @@ -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;
Expand All @@ -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');
}
Expand All @@ -116,7 +135,7 @@ export class SbbOptGroupElement extends LitElement {
<div class="sbb-optgroup__icon-space"></div>
<span>${this.label}</span>
</div>
<slot @slotchange=${this._proxyDisabledToOptions}></slot>
<slot></slot>
`;
}
}
Expand Down
126 changes: 126 additions & 0 deletions src/components/option/option/option.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -75,5 +76,130 @@ describe('sbb-option', () => {
</span>
`);
});

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(`
<span class="sbb-option__label">
<slot></slot>
<span class="sbb-option__label--highlight"></span>
<span>Opt</span>
<span class="sbb-option__label--highlight">ion 1</span>
</span>
`);

options[0].textContent = 'Other content';
await waitForLitRender(autocomplete);

expect(optionOneLabel).dom.to.be.equal(`
<span class="sbb-option__label">
<slot></slot>
Other content
</span>
`);

options[0].textContent = 'Option';
await waitForLitRender(autocomplete);

expect(optionOneLabel).dom.to.be.equal(`
<span class="sbb-option__label">
<slot></slot>
<span class="sbb-option__label--highlight"></span>
<span>Opt</span>
<span class="sbb-option__label--highlight">ion</span>
</span>
`);
});

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(`
<span class="sbb-option__label">
<slot></slot>
<span class="sbb-option__label--highlight"></span>
<span>Opt</span>
<span class="sbb-option__label--highlight">ion 1</span>
</span>
`);

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(`
<span class="sbb-option__label">
<slot></slot>
<span class="sbb-option__label--highlight"></span>
<span>Opt</span>
<span class="sbb-option__label--highlight">ion 4</span>
</span>
`);
});

it('highlight later added options in sbb-optgroup', async () => {
element = await fixture(html`
<sbb-form-field>
<input />
<sbb-autocomplete>
<sbb-optgroup>
<sbb-option id="option-1" value="1">Option 1</sbb-option>
</sbb-optgroup>
</sbb-autocomplete>
</sbb-form-field>
`);

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(`
<span class="sbb-option__label">
<slot></slot>
<span class="sbb-option__label--highlight"></span>
<span>Opt</span>
<span class="sbb-option__label--highlight">ion 1</span>
</span>
`);

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(`
<span class="sbb-option__label">
<slot></slot>
<span class="sbb-option__label--highlight"></span>
<span>Opt</span>
<span class="sbb-option__label--highlight">ion 2</span>
</span>
`);
});
});
});

0 comments on commit 76affb1

Please sign in to comment.