fix(select): set formattedValue to text of selected option

This commit is contained in:
qa46hx 2021-07-21 09:18:43 +02:00 committed by Thomas Allmer
parent 26b2f4e0e5
commit 09ee55cbee
4 changed files with 65 additions and 25 deletions

View file

@ -0,0 +1,6 @@
---
'@lion/form-integrations': patch
'@lion/select': patch
---
Set formattedValue to the text of the selected option instead of its value

View file

@ -54,7 +54,7 @@ describe('Form Integrations', () => {
favoriteFruit: 'Banana', favoriteFruit: 'Banana',
favoriteMovie: 'Rocky', favoriteMovie: 'Rocky',
favoriteColor: 'hotpink', favoriteColor: 'hotpink',
lyrics: '1', lyrics: 'Fire up that loud',
range: 2.3, range: 2.3,
terms: [], terms: [],
notifications: '', notifications: '',

View file

@ -1,6 +1,9 @@
/* eslint-disable max-classes-per-file */ /* eslint-disable max-classes-per-file */
import { LionField } from '@lion/form-core'; import { LionField } from '@lion/form-core';
/**
* @typedef {import('@lion/localize/types/LocalizeMixinTypes').FormatNumberOptions} FormatOptions
*/
class LionFieldWithSelect extends LionField { class LionFieldWithSelect extends LionField {
/** @type {any} */ /** @type {any} */
static get properties() { static get properties() {
@ -28,19 +31,19 @@ class LionFieldWithSelect extends LionField {
} }
/** /**
* LionSelectNative: wraps the native HTML element select * LionSelect: wraps the native HTML element select
* *
* <lion-select-native> * <lion-select>
* <label slot="label">My Input</label> * <label slot="label">My Input</label>
* <select slot="input"> * <select slot="input">
* <option value="top">top</option> * <option value="top">top</option>
* <option value="bottom">bottom</option> * <option value="bottom">bottom</option>
* </select> * </select>
* </lion-select-native> * </lion-select>
* *
* You can preselect an option by setting the property modelValue. * You can preselect an option by setting the property modelValue.
* Example: * Example:
* <lion-select-native modelValue="${'<value of option 2>'}"> * <lion-select .modelValue="${'<value of option 2>'}">
* *
* It extends LionField so it inherits required and disabled. * It extends LionField so it inherits required and disabled.
* *
@ -59,24 +62,6 @@ export class LionSelect extends LionFieldWithSelect {
this._inputNode.addEventListener('change', this._proxyChangeEvent); this._inputNode.addEventListener('change', this._proxyChangeEvent);
} }
// FIXME: For some reason we have to override this FormatMixin getter/setter pair for the tests to pass
get value() {
return (this._inputNode && this._inputNode.value) || this.__value || '';
}
// We don't delegate, because we want to preserve caret position via _setValueAndPreserveCaret
/** @type {string} */
set value(value) {
// if not yet connected to dom can't change the value
if (this._inputNode) {
this._inputNode.value = value;
/** @type {string | undefined} */
this.__value = undefined;
} else {
this.__value = value;
}
}
/** @param {import('@lion/core').PropertyValues } changedProperties */ /** @param {import('@lion/core').PropertyValues } changedProperties */
updated(changedProperties) { updated(changedProperties) {
super.updated(changedProperties); super.updated(changedProperties);
@ -99,6 +84,28 @@ export class LionSelect extends LionFieldWithSelect {
this._inputNode.removeEventListener('change', this._proxyChangeEvent); this._inputNode.removeEventListener('change', this._proxyChangeEvent);
} }
/**
* @override FormatMixin - set formattedValue to selected option text
* @param {*} v - modelValue: can be an Object, Number, String depending on the
* input type(date, number, email etc)
* @returns {string} formattedValue
*/
formatter(v) {
// The selectedIndex is not yet updated
const found = Array.from(this._inputNode.options).find(option => option.value === v);
return found ? found.text : '';
}
/**
* @override FormatMixin - set value equal to modelValue instead of formattedValue
*/
_reflectBackFormattedValueToUser() {
if (this._reflectBackOn()) {
// Text 'undefined' should not end up in <input>
this.value = typeof this.modelValue !== 'undefined' ? this.modelValue : '';
}
}
/** @protected */ /** @protected */
_proxyChangeEvent() { _proxyChangeEvent() {
this.dispatchEvent( this.dispatchEvent(

View file

@ -1,7 +1,10 @@
import { expect, fixture } from '@open-wc/testing'; import '@lion/select/define';
import { aTimeout, expect, fixture } from '@open-wc/testing';
import { html } from 'lit/static-html.js'; import { html } from 'lit/static-html.js';
import '@lion/select/define'; /**
* @typedef {import('../src/LionSelect').LionSelect} LionSelect
*/
describe('lion-select', () => { describe('lion-select', () => {
it('can preselect an option', async () => { it('can preselect an option', async () => {
@ -17,6 +20,30 @@ describe('lion-select', () => {
expect(lionSelect.querySelector('select')?.value).to.equal('nr2'); expect(lionSelect.querySelector('select')?.value).to.equal('nr2');
}); });
it('sets the values correctly based on the option value and view value', async () => {
const lionSelect = /** @type {LionSelect} */ (
await fixture(html`
<lion-select label="Foo item" .modelValue="${'nr2'}">
<select slot="input">
<option value="nr1">Item 1</option>
<option value="nr2">Item 2</option>
<option value="nr3"></option>
</select>
</lion-select>
`)
);
expect(lionSelect.serializedValue).to.equal('nr2');
expect(lionSelect.formattedValue).to.equal('Item 2');
lionSelect.modelValue = 'nr1';
await aTimeout;
expect(lionSelect.serializedValue).to.equal('nr1');
expect(lionSelect.formattedValue).to.equal('Item 1');
lionSelect.modelValue = 'nr3';
await aTimeout;
expect(lionSelect.serializedValue).to.equal('nr3');
expect(lionSelect.formattedValue).to.equal('');
});
it('is accessible', async () => { it('is accessible', async () => {
const lionSelect = await fixture(html` const lionSelect = await fixture(html`
<lion-select .modelValue="${'nr2'}"> <lion-select .modelValue="${'nr2'}">