fix(select-rich): trigger manual update invoker selected elem on sync

This commit is contained in:
jorenbroekema 2020-10-15 17:54:57 +02:00 committed by Thomas Allmer
parent f7ab53910d
commit 1671c705b8
4 changed files with 51 additions and 6 deletions

View file

@ -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.

View file

@ -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');
}
}

View file

@ -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`
<lion-select-rich>
${firstOption}
<${tagString} .choiceValue=${20}></${tagString}>
</lion-select-rich>
`));
// @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', () => {

View file

@ -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('<lion-textarea>', () => {
const resizeSpy = sinon.spy(textArea, 'resizeTextarea');
el.style.display = 'block';
await nextFrame();
await aTimeout(0);
expect(resizeSpy.calledOnce).to.be.true;
});