diff --git a/src/elements/option/option/option-base-element.ts b/src/elements/option/option/option-base-element.ts
index 0674c3d60d..7ae42efd5b 100644
--- a/src/elements/option/option/option-base-element.ts
+++ b/src/elements/option/option/option-base-element.ts
@@ -261,13 +261,19 @@ abstract class SbbOptionBaseElement extends SbbDisabledMixin(
return nothing;
}
+ private _handleSlotChange(): void {
+ this.handleHighlightState();
+ /** @internal */
+ this.dispatchEvent(new Event('optionLabelChanged', { bubbles: true }));
+ }
+
protected override render(): TemplateResult {
return html`
${this.renderIcon()}
-
+
${this.renderLabel()}
${this._inertAriaGroups && this.getAttribute('data-group-label')
? html`
diff --git a/src/elements/select/select.spec.ts b/src/elements/select/select.spec.ts
index 9df1e1ca32..d6d727de6b 100644
--- a/src/elements/select/select.spec.ts
+++ b/src/elements/select/select.spec.ts
@@ -470,6 +470,56 @@ describe(`sbb-select`, () => {
expect(element).to.have.attribute('data-state', 'opened');
});
+
+ it('updates displayed value on option value change', async () => {
+ expect(displayValue.textContent!.trim()).to.be.equal('Placeholder');
+ firstOption.click();
+ await waitForLitRender(element);
+ displayValue = element.shadowRoot!.querySelector('.sbb-select__trigger')!;
+
+ expect(displayValue.textContent!.trim()).to.be.equal('First');
+
+ firstOption.textContent = 'First modified';
+ await waitForLitRender(element);
+ displayValue = element.shadowRoot!.querySelector('.sbb-select__trigger')!;
+
+ expect(displayValue.textContent!.trim()).to.be.equal('First modified');
+
+ // Deselection
+ element.value = '';
+ await waitForLitRender(element);
+ displayValue = element.shadowRoot!.querySelector('.sbb-select__trigger')!;
+
+ expect(displayValue.textContent!.trim()).to.be.equal('Placeholder');
+ });
+
+ it('updates displayed value on option value change if multiple', async () => {
+ element.multiple = true;
+ await waitForLitRender(element);
+
+ expect(displayValue.textContent!.trim()).to.be.equal('Placeholder');
+
+ firstOption.click();
+ secondOption.click();
+ await waitForLitRender(element);
+ displayValue = element.shadowRoot!.querySelector('.sbb-select__trigger')!;
+
+ expect(displayValue.textContent!.trim()).to.be.equal('First, Second');
+
+ firstOption.textContent = 'First modified';
+ await waitForLitRender(element);
+ displayValue = element.shadowRoot!.querySelector('.sbb-select__trigger')!;
+
+ expect(displayValue.textContent!.trim()).to.be.equal('First modified, Second');
+
+ // Deselection
+ firstOption.click();
+ secondOption.click();
+ await waitForLitRender(element);
+ displayValue = element.shadowRoot!.querySelector('.sbb-select__trigger')!;
+
+ expect(displayValue.textContent!.trim()).to.be.equal('Placeholder');
+ });
});
describe('form association', () => {
diff --git a/src/elements/select/select.ts b/src/elements/select/select.ts
index c3738d9ddb..df39866159 100644
--- a/src/elements/select/select.ts
+++ b/src/elements/select/select.ts
@@ -265,27 +265,53 @@ class SbbSelectElement extends SbbUpdateSchedulerMixin(
}
}
+ /** Listens to option changes. */
+ private _onOptionLabelChanged(event: Event): void {
+ const target = event.target as SbbOptionElement;
+ const selected = this._getSelected();
+
+ if (
+ (!Array.isArray(selected) && target !== selected) ||
+ (Array.isArray(selected) && !selected.includes(target))
+ ) {
+ return;
+ }
+
+ this._updateDisplayValue(selected);
+ }
+
+ private _updateDisplayValue(selected: SbbOptionElement | SbbOptionElement[] | null): void {
+ if (Array.isArray(selected)) {
+ this._displayValue = selected.map((o) => o.textContent).join(', ') || null;
+ } else if (selected) {
+ this._displayValue = selected?.textContent || null;
+ } else {
+ this._displayValue = null;
+ }
+ }
+
/** Sets the _displayValue by checking the internal sbb-options and setting the correct `selected` value on them. */
private _onValueChanged(newValue: string | string[]): void {
const options = this._filteredOptions;
if (!Array.isArray(newValue)) {
- const optionElement = options.find((o) => (o.value ?? o.getAttribute('value')) === newValue);
+ const optionElement =
+ options.find((o) => (o.value ?? o.getAttribute('value')) === newValue) ?? null;
if (optionElement) {
optionElement.selected = true;
}
options
.filter((o) => (o.value ?? o.getAttribute('value')) !== newValue)
.forEach((o) => (o.selected = false));
- this._displayValue = optionElement?.textContent || null;
+ this._updateDisplayValue(optionElement);
} else {
options
.filter((o) => !newValue.includes(o.value ?? o.getAttribute('value')))
.forEach((e) => (e.selected = false));
- const selectedOptionElements = options.filter((o) =>
+ const selectedElements = options.filter((o) =>
newValue.includes(o.value ?? o.getAttribute('value')),
);
- selectedOptionElements.forEach((o) => (o.selected = true));
- this._displayValue = selectedOptionElements.map((o) => o.textContent).join(', ') || null;
+ selectedElements.forEach((o) => (o.selected = true));
+ this._updateDisplayValue(selectedElements);
}
this._stateChange.emit({ type: 'value', value: newValue });
}
@@ -352,6 +378,11 @@ class SbbSelectElement extends SbbUpdateSchedulerMixin(
(e: CustomEvent) => this._onOptionChanged(e),
{ signal },
);
+
+ this.addEventListener('optionLabelChanged', (e: Event) => this._onOptionLabelChanged(e), {
+ signal,
+ });
+
this.addEventListener(
'click',
(e: MouseEvent) => {
@@ -761,24 +792,28 @@ class SbbSelectElement extends SbbUpdateSchedulerMixin(
}
};
- private _setValueFromSelectedOption(): void {
- if (!this.multiple) {
- const selectedOption = this._filteredOptions.find((option) => option.selected);
- if (selectedOption) {
- this._activeItemIndex = this._filteredOptions.findIndex(
- (option) => option === selectedOption,
- );
- this.value = selectedOption.value;
- }
- } else {
- const options = this._filteredOptions.filter((option) => option.selected);
- if (options && options.length > 0) {
+ private _setValueFromSelected(): void {
+ const selected = this._getSelected();
+
+ if (Array.isArray(selected)) {
+ if (selected && selected.length > 0) {
const value: string[] = [];
- for (const option of options) {
+ for (const option of selected) {
value.push(option.value!);
}
this.value = value;
}
+ } else if (selected) {
+ this._activeItemIndex = this._filteredOptions.findIndex((option) => option === selected);
+ this.value = selected.value;
+ }
+ }
+
+ private _getSelected(): SbbOptionElement | SbbOptionElement[] | null {
+ if (this.multiple) {
+ return this._filteredOptions.filter((option) => option.selected);
+ } else {
+ return this._filteredOptions.find((option) => option.selected) ?? null;
}
}
@@ -860,7 +895,7 @@ class SbbSelectElement extends SbbUpdateSchedulerMixin(
?aria-multiselectable=${this.multiple}
${ref((containerRef) => (this._optionContainer = containerRef as HTMLElement))}
>
-
+