Skip to content

Commit d2035e6

Browse files
committed
feat(field): add reset method and capture inital model value
1 parent 84a4227 commit d2035e6

File tree

2 files changed

+68
-60
lines changed

2 files changed

+68
-60
lines changed

packages/field/src/LionField.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,11 @@ export class LionField extends FormControlMixin(
101101
this.submitted = false;
102102
}
103103

104+
firstUpdated(c) {
105+
super.firstUpdated(c);
106+
this._initialModelValue = this.modelValue;
107+
}
108+
104109
connectedCallback() {
105110
// TODO: Normally we put super calls on top for predictability,
106111
// here we temporarily need to do attribute delegation before,
@@ -164,6 +169,11 @@ export class LionField extends FormControlMixin(
164169
this.submitted = false;
165170
}
166171

172+
reset() {
173+
this.modelValue = this._initialModelValue;
174+
this.resetInteractionState();
175+
}
176+
167177
clear() {
168178
if (super.clear) {
169179
// Let validationMixin and interactionStateMixin clear their

packages/field/test/lion-field.test.js

Lines changed: 58 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,12 @@ beforeEach(() => {
3131

3232
describe('<lion-field>', () => {
3333
it(`puts a unique id "${tagString}-[hash]" on the native input`, async () => {
34-
const el = await fixture(`<${tagString}>${inputSlotString}</${tagString}>`);
34+
const el = await fixture(html`<${tag}>${inputSlot}</${tag}>`);
3535
expect(el.$$slot('input').id).to.equal(el._inputId);
3636
});
3737

3838
it('fires focus/blur event on host and native input if focused/blurred', async () => {
39-
const el = await fixture(`<${tagString}>${inputSlotString}</${tagString}>`);
39+
const el = await fixture(html`<${tag}>${inputSlot}</${tag}>`);
4040
const cbFocusHost = sinon.spy();
4141
el.addEventListener('focus', cbFocusHost);
4242
const cbFocusNativeInput = sinon.spy();
@@ -68,32 +68,31 @@ describe('<lion-field>', () => {
6868
expect(cbBlurNativeInput.callCount).to.equal(2);
6969
});
7070

71+
it('offers simple getter "this.focused" returning true/false for the current focus state', async () => {
72+
const el = await fixture(html`<${tag}>${inputSlot}</${tag}>`);
73+
expect(el.focused).to.equal(false);
74+
await triggerFocusFor(el);
75+
expect(el.focused).to.equal(true);
76+
await triggerBlurFor(el);
77+
expect(el.focused).to.equal(false);
78+
});
79+
7180
it('can be disabled via attribute', async () => {
72-
const elDisabled = await fixture(`<${tagString} disabled>${inputSlotString}</${tagString}>`);
81+
const elDisabled = await fixture(html`<${tag} disabled>${inputSlot}</${tag}>`);
7382
expect(elDisabled.disabled).to.equal(true);
7483
expect(elDisabled.inputElement.disabled).to.equal(true);
7584
});
7685

7786
it('can be disabled via property', async () => {
78-
const el = await fixture(`<${tagString}>${inputSlotString}</${tagString}>`);
87+
const el = await fixture(html`<${tag}>${inputSlot}</${tag}>`);
7988
el.disabled = true;
8089
await el.updateComplete;
8190
expect(el.inputElement.disabled).to.equal(true);
8291
});
8392

84-
// classes are added only for backward compatibility - they are deprecated
85-
it('sets a state-disabled class when disabled', async () => {
86-
const el = await fixture(`<${tagString} disabled>${inputSlotString}</${tagString}>`);
87-
await el.updateComplete;
88-
expect(el.classList.contains('state-disabled')).to.equal(true);
89-
el.disabled = false;
90-
await el.updateComplete;
91-
expect(el.classList.contains('state-disabled')).to.equal(false);
92-
});
93-
9493
it('can be cleared which erases value, validation and interaction states', async () => {
9594
const el = await fixture(
96-
`<${tagString} value="Some value from attribute">${inputSlotString}</${tagString}>`,
95+
html`<${tag} value="Some value from attribute">${inputSlot}</${tag}>`,
9796
);
9897
el.clear();
9998
expect(el.value).to.equal('');
@@ -103,35 +102,34 @@ describe('<lion-field>', () => {
103102
expect(el.value).to.equal('');
104103
});
105104

105+
it('can be reset which restores original modelValue', async () => {
106+
const el = await fixture(html`
107+
<${tag} .modelValue="${'foo'}">
108+
${inputSlot}
109+
</${tag}>`);
110+
expect(el._initialModelValue).to.equal('foo');
111+
el.modelValue = 'bar';
112+
el.reset();
113+
expect(el.modelValue).to.equal('foo');
114+
});
115+
106116
it('reads initial value from attribute value', async () => {
107-
const el = await fixture(`<${tagString} value="one">${inputSlotString}</${tagString}>`);
117+
const el = await fixture(html`<${tag} value="one">${inputSlot}</${tag}>`);
108118
expect(el.$$slot('input').value).to.equal('one');
109119
});
110120

111121
it('delegates value property', async () => {
112-
const el = await fixture(`<${tagString}>${inputSlotString}</${tagString}>`);
122+
const el = await fixture(html`<${tag}>${inputSlot}</${tag}>`);
113123
expect(el.$$slot('input').value).to.equal('');
114124
el.value = 'one';
115125
expect(el.value).to.equal('one');
116126
expect(el.$$slot('input').value).to.equal('one');
117127
});
118128

119-
it('has a name which is reflected to an attribute and is synced down to the native input', async () => {
120-
const el = await fixture(`<${tagString}>${inputSlotString}</${tagString}>`);
121-
expect(el.name).to.equal('');
122-
expect(el.getAttribute('name')).to.equal('');
123-
expect(el.inputElement.getAttribute('name')).to.equal('');
124-
125-
el.name = 'foo';
126-
await el.updateComplete;
127-
expect(el.getAttribute('name')).to.equal('foo');
128-
expect(el.inputElement.getAttribute('name')).to.equal('foo');
129-
});
130-
131129
// TODO: find out if we could put all listeners on this.value (instead of this.inputElement.value)
132130
// and make it act on this.value again
133131
it('has a class "state-filled" if this.value is filled', async () => {
134-
const el = await fixture(`<${tagString} value="filled">${inputSlotString}</${tagString}>`);
132+
const el = await fixture(html`<${tag} value="filled">${inputSlot}</${tag}>`);
135133
expect(el.classList.contains('state-filled')).to.equal(true);
136134
el.value = '';
137135
await el.updateComplete;
@@ -142,7 +140,7 @@ describe('<lion-field>', () => {
142140
});
143141

144142
it('preserves the caret position on value change for native text fields (input|textarea)', async () => {
145-
const el = await fixture(`<${tagString}>${inputSlotString}</${tagString}>`);
143+
const el = await fixture(html`<${tag}>${inputSlot}</${tag}>`);
146144
await triggerFocusFor(el);
147145
await el.updateComplete;
148146
el.inputElement.value = 'hello world';
@@ -155,7 +153,7 @@ describe('<lion-field>', () => {
155153

156154
// TODO: add pointerEvents test for disabled
157155
it('has a class "state-disabled"', async () => {
158-
const el = await fixture(`<${tagString}>${inputSlotString}</${tagString}>`);
156+
const el = await fixture(html`<${tag}>${inputSlot}</${tag}>`);
159157
expect(el.classList.contains('state-disabled')).to.equal(false);
160158
expect(el.inputElement.hasAttribute('disabled')).to.equal(false);
161159

@@ -166,7 +164,7 @@ describe('<lion-field>', () => {
166164
expect(el.classList.contains('state-disabled')).to.equal(true);
167165
expect(el.inputElement.hasAttribute('disabled')).to.equal(true);
168166

169-
const disabledel = await fixture(`<${tagString} disabled>${inputSlotString}</${tagString}>`);
167+
const disabledel = await fixture(html`<${tag} disabled>${inputSlot}</${tag}>`);
170168
expect(disabledel.classList.contains('state-disabled')).to.equal(true);
171169
expect(disabledel.inputElement.hasAttribute('disabled')).to.equal(true);
172170
});
@@ -186,12 +184,12 @@ describe('<lion-field>', () => {
186184
<div slot="feedback" id="feedback-[id]">[feedback] </span>
187185
</lion-field>
188186
~~~`, async () => {
189-
const el = await fixture(`<${tagString}>
187+
const el = await fixture(html`<${tag}>
190188
<label slot="label">My Name</label>
191-
${inputSlotString}
189+
${inputSlot}
192190
<span slot="help-text">Enter your Name</span>
193191
<span slot="feedback">No name entered</span>
194-
</${tagString}>
192+
</${tag}>
195193
`);
196194
const nativeInput = el.$$slot('input');
197195

@@ -202,13 +200,13 @@ describe('<lion-field>', () => {
202200

203201
it(`allows additional slots (prefix, suffix, before, after) to be included in labelledby
204202
(via attribute data-label) and in describedby (via attribute data-description)`, async () => {
205-
const el = await fixture(`<${tagString}>
206-
${inputSlotString}
203+
const el = await fixture(html`<${tag}>
204+
${inputSlot}
207205
<span slot="before" data-label>[before]</span>
208206
<span slot="after" data-label>[after]</span>
209207
<span slot="prefix" data-description>[prefix]</span>
210208
<span slot="suffix" data-description>[suffix]</span>
211-
</${tagString}>
209+
</${tag}>
212210
`);
213211

214212
const nativeInput = el.$$slot('input');
@@ -223,45 +221,45 @@ describe('<lion-field>', () => {
223221
// TODO: put this test on FormControlMixin test once there
224222
it(`allows to add to aria description or label via addToAriaLabel() and
225223
addToAriaDescription()`, async () => {
226-
const wrapper = await fixture(`
224+
const wrapper = await fixture(html`
227225
<div id="wrapper">
228-
<${tagString}>
229-
${inputSlotString}
226+
<${tag}>
227+
${inputSlot}
230228
<label slot="label">Added to label by default</label>
231229
<div slot="feedback">Added to description by default</div>
232-
</${tagString}>
230+
</${tag}>
233231
<div id="additionalLabel"> This also needs to be read whenever the input has focus</div>
234232
<div id="additionalDescription"> Same for this </div>
235233
</div>`);
236-
const el = wrapper.querySelector(`${tagString}`);
234+
const el = wrapper.querySelector(tagString);
237235
// wait until the field element is done rendering
238236
await el.updateComplete;
237+
await el.updateComplete;
239238

240239
const { inputElement } = el;
241-
const get = by => inputElement.getAttribute(`aria-${by}`);
242240

243241
// 1. addToAriaLabel()
244242
// Check if the aria attr is filled initially
245-
expect(get('labelledby')).to.contain(`label-${el._inputId}`);
243+
expect(inputElement.getAttribute('aria-labelledby')).to.contain(`label-${el._inputId}`);
246244
el.addToAriaLabel('additionalLabel');
247245
// Now check if ids are added to the end (not overridden)
248-
expect(get('labelledby')).to.contain(`label-${el._inputId}`);
246+
expect(inputElement.getAttribute('aria-labelledby')).to.contain(`label-${el._inputId}`);
249247
// Should be placed in the end
250248
expect(
251-
get('labelledby').indexOf(`label-${el._inputId}`) <
252-
get('labelledby').indexOf('additionalLabel'),
249+
inputElement.getAttribute('aria-labelledby').indexOf(`label-${el._inputId}`) <
250+
inputElement.getAttribute('aria-labelledby').indexOf('additionalLabel'),
253251
);
254252

255253
// 2. addToAriaDescription()
256254
// Check if the aria attr is filled initially
257-
expect(get('describedby')).to.contain(`feedback-${el._inputId}`);
255+
expect(inputElement.getAttribute('aria-describedby')).to.contain(`feedback-${el._inputId}`);
258256
el.addToAriaDescription('additionalDescription');
259257
// Now check if ids are added to the end (not overridden)
260-
expect(get('describedby')).to.contain(`feedback-${el._inputId}`);
258+
expect(inputElement.getAttribute('aria-describedby')).to.contain(`feedback-${el._inputId}`);
261259
// Should be placed in the end
262260
expect(
263-
get('describedby').indexOf(`feedback-${el._inputId}`) <
264-
get('describedby').indexOf('additionalDescription'),
261+
inputElement.getAttribute('aria-describedby').indexOf(`feedback-${el._inputId}`) <
262+
inputElement.getAttribute('aria-describedby').indexOf('additionalDescription'),
265263
);
266264
});
267265
});
@@ -285,7 +283,7 @@ describe('<lion-field>', () => {
285283
function hasX(str) {
286284
return { hasX: str.indexOf('x') > -1 };
287285
}
288-
const el = await fixture(`<${tagString}>${inputSlotString}</${tagString}>`);
286+
const el = await fixture(html`<${tag}>${inputSlot}</${tag}>`);
289287
const feedbackEl = el._feedbackElement;
290288

291289
el.modelValue = 'a@b.nl';
@@ -355,17 +353,17 @@ describe('<lion-field>', () => {
355353

356354
describe(`Content projection${nameSuffix}`, () => {
357355
it('renders correctly all slot elements in light DOM', async () => {
358-
const el = await fixture(`
359-
<${tagString}>
356+
const el = await fixture(html`
357+
<${tag}>
360358
<label slot="label">[label]</label>
361-
${inputSlotString}
359+
${inputSlot}
362360
<span slot="help-text">[help-text]</span>
363361
<span slot="before">[before]</span>
364362
<span slot="after">[after]</span>
365363
<span slot="prefix">[prefix]</span>
366364
<span slot="suffix">[suffix]</span>
367365
<span slot="feedback">[feedback]</span>
368-
</${tagString}>
366+
</${tag}>
369367
`);
370368

371369
const names = [
@@ -388,9 +386,9 @@ describe('<lion-field>', () => {
388386
});
389387
});
390388

391-
describe(`Delegation${nameSuffix}`, () => {
389+
describe('Delegation', () => {
392390
it('delegates property value', async () => {
393-
const el = await fixture(`<${tagString}>${inputSlotString}</${tagString}>`);
391+
const el = await fixture(html`<${tag}>${inputSlot}</${tag}>`);
394392
expect(el.inputElement.value).to.equal('');
395393
el.value = 'one';
396394
expect(el.value).to.equal('one');

0 commit comments

Comments
 (0)