From 1671c705b8df8fe2f9de191739d732bd3f586a39 Mon Sep 17 00:00:00 2001 From: jorenbroekema Date: Thu, 15 Oct 2020 17:54:57 +0200 Subject: [PATCH] fix(select-rich): trigger manual update invoker selected elem on sync --- .changeset/tricky-pumpkins-lie.md | 5 +++ packages/select-rich/src/LionSelectRich.js | 7 ++++ .../select-rich/test/lion-select-rich.test.js | 41 +++++++++++++++++-- packages/textarea/test/lion-textarea.test.js | 4 +- 4 files changed, 51 insertions(+), 6 deletions(-) create mode 100644 .changeset/tricky-pumpkins-lie.md 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}>`, + ); + + const el = /** @type {LionSelectRich} */ (await fixture(html` + + ${firstOption} + <${tagString} .choiceValue=${20}> + + `)); + + // @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; });