diff --git a/.changeset/tricky-pumpkins-lie.md b/.changeset/tricky-pumpkins-lie.md
new file mode 100644
index 000000000..0e0a495b3
--- /dev/null
+++ b/.changeset/tricky-pumpkins-lie.md
@@ -0,0 +1,5 @@
+---
+'@lion/select-rich': patch
+---
+
+Fix select rich to manually request update for the invoker selected element when it synchronizes, as the modelValue could be changed but would not trigger a change since the old and new value are both referenced from the updated node reference, meaning they will always be the same and never pass the dirty check.
diff --git a/packages/select-rich/src/LionSelectRich.js b/packages/select-rich/src/LionSelectRich.js
index 1776e4231..eb7e4dc67 100644
--- a/packages/select-rich/src/LionSelectRich.js
+++ b/packages/select-rich/src/LionSelectRich.js
@@ -265,6 +265,13 @@ export class LionSelectRich extends SlotMixin(ScopedElementsMixin(OverlayMixin(L
this._invokerNode.selectedElement = this.formElements[
/** @type {number} */ (this.checkedIndex)
];
+ /**
+ * Manually update this, as the node reference may be the same, but the modelValue might not.
+ * This would mean that it won't pass the LitElement dirty check.
+ * hasChanged in selectedElement won't work, since the oldValue and the newValue's modelValues will be the same,
+ * as they are referenced through the same node reference.
+ */
+ this._invokerNode.requestUpdate('selectedElement');
}
}
diff --git a/packages/select-rich/test/lion-select-rich.test.js b/packages/select-rich/test/lion-select-rich.test.js
index 1f6689ff1..b3effaba0 100644
--- a/packages/select-rich/test/lion-select-rich.test.js
+++ b/packages/select-rich/test/lion-select-rich.test.js
@@ -1,5 +1,8 @@
import { LitElement } from '@lion/core';
+// @ts-expect-error
+import { renderLitAsNode } from '@lion/helpers';
import { OverlayController } from '@lion/overlays';
+import { LionOption } from '@lion/listbox';
import {
aTimeout,
defineCE,
@@ -16,10 +19,6 @@ import '@lion/listbox/lion-option.js';
import '@lion/listbox/lion-options.js';
import '../lion-select-rich.js';
-/**
- * @typedef {import('@lion/listbox').LionOption} LionOption
- */
-
/**
* @param {LionSelectRich} lionSelectEl
*/
@@ -155,6 +154,40 @@ describe('lion-select-rich', () => {
// @ts-ignore allow protected access in tests
expect(el._invokerNode.hasAttribute('single-option')).to.be.true;
});
+
+ it('updates the invoker when the selected element is the same but the modelValue was updated asynchronously', async () => {
+ const tag = defineCE(
+ class LionCustomOption extends LionOption {
+ render() {
+ return html`${this.modelValue.value}`;
+ }
+
+ createRenderRoot() {
+ return this;
+ }
+ },
+ );
+ const tagString = unsafeStatic(tag);
+
+ const firstOption = renderLitAsNode(
+ html`<${tagString} checked .choiceValue=${10}>${tagString}>`,
+ );
+
+ const el = /** @type {LionSelectRich} */ (await fixture(html`
+
+ ${firstOption}
+ <${tagString} .choiceValue=${20}>${tagString}>
+
+ `));
+
+ // @ts-ignore allow protected access in tests
+ expect(el._invokerNode.shadowRoot.firstElementChild.textContent).to.equal('10');
+
+ firstOption.modelValue = { value: 30, checked: true };
+ await el.updateComplete;
+ // @ts-ignore allow protected access in tests
+ expect(el._invokerNode.shadowRoot.firstElementChild.textContent).to.equal('30');
+ });
});
describe('overlay', () => {
diff --git a/packages/textarea/test/lion-textarea.test.js b/packages/textarea/test/lion-textarea.test.js
index 5abe4efcc..c9926cf40 100644
--- a/packages/textarea/test/lion-textarea.test.js
+++ b/packages/textarea/test/lion-textarea.test.js
@@ -1,4 +1,4 @@
-import { expect, fixture as _fixture, html, nextFrame } from '@open-wc/testing';
+import { aTimeout, expect, fixture as _fixture, html } from '@open-wc/testing';
import sinon from 'sinon';
import '../lion-textarea.js';
@@ -159,7 +159,7 @@ describe('', () => {
const resizeSpy = sinon.spy(textArea, 'resizeTextarea');
el.style.display = 'block';
- await nextFrame();
+ await aTimeout(0);
expect(resizeSpy.calledOnce).to.be.true;
});