@dile/ui 3.7.0 → 3.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/components/input/__screenshots__/input-number.spec.js/dile-input-number-normalizes-separator-only-on-blur-when-normalizeOn-is-blur-1.png +0 -0
- package/components/input/input-number.spec.js +17 -4
- package/components/input/src/DileInputNumber.js +43 -22
- package/mixins/form/src/DileForm.js +11 -8
- package/mixins/form/src/DileForm.spec.js +29 -0
- package/package.json +2 -2
|
Binary file
|
|
@@ -33,7 +33,7 @@ describe('dile-input-number', () => {
|
|
|
33
33
|
input.dispatchEvent(new Event('input'));
|
|
34
34
|
await el.updateComplete;
|
|
35
35
|
|
|
36
|
-
expect(el.value).toBe('12
|
|
36
|
+
expect(el.value).toBe('12.75');
|
|
37
37
|
expect(input.value).toBe('12,75');
|
|
38
38
|
});
|
|
39
39
|
|
|
@@ -57,7 +57,7 @@ describe('dile-input-number', () => {
|
|
|
57
57
|
input.dispatchEvent(new Event('blur'));
|
|
58
58
|
await el.updateComplete;
|
|
59
59
|
|
|
60
|
-
expect(el.value).toBe('1
|
|
60
|
+
expect(el.value).toBe('1.25');
|
|
61
61
|
expect(input.value).toBe('1,25');
|
|
62
62
|
});
|
|
63
63
|
|
|
@@ -65,6 +65,7 @@ describe('dile-input-number', () => {
|
|
|
65
65
|
const el = await renderInputNumber('<dile-input-number name="amount" decimalSeparator="," normalizeOn="blur"></dile-input-number>');
|
|
66
66
|
const input = el.shadowRoot.querySelector('input');
|
|
67
67
|
|
|
68
|
+
input.dispatchEvent(new Event('focus'));
|
|
68
69
|
input.value = '12.75';
|
|
69
70
|
input.dispatchEvent(new Event('input'));
|
|
70
71
|
await el.updateComplete;
|
|
@@ -75,7 +76,7 @@ describe('dile-input-number', () => {
|
|
|
75
76
|
input.dispatchEvent(new Event('blur'));
|
|
76
77
|
await el.updateComplete;
|
|
77
78
|
|
|
78
|
-
expect(el.value).toBe('12
|
|
79
|
+
expect(el.value).toBe('12.75');
|
|
79
80
|
expect(input.value).toBe('12,75');
|
|
80
81
|
});
|
|
81
82
|
|
|
@@ -90,7 +91,19 @@ describe('dile-input-number', () => {
|
|
|
90
91
|
input.dispatchEvent(new Event('input'));
|
|
91
92
|
await el.updateComplete;
|
|
92
93
|
|
|
93
|
-
expect(el.value).toBe('12
|
|
94
|
+
expect(el.value).toBe('12.75');
|
|
94
95
|
expect(input.value).toBe('12,75');
|
|
95
96
|
});
|
|
97
|
+
|
|
98
|
+
it('keeps canonical value when normalizeOn fallback applies', async () => {
|
|
99
|
+
const el = await renderInputNumber('<dile-input-number name="amount" decimalSeparator="," normalizeOn="foo"></dile-input-number>');
|
|
100
|
+
const input = el.shadowRoot.querySelector('input');
|
|
101
|
+
|
|
102
|
+
input.value = '8,5';
|
|
103
|
+
input.dispatchEvent(new Event('input'));
|
|
104
|
+
await el.updateComplete;
|
|
105
|
+
|
|
106
|
+
expect(el.value).toBe('8.5');
|
|
107
|
+
expect(input.value).toBe('8,5');
|
|
108
|
+
});
|
|
96
109
|
});
|
|
@@ -37,6 +37,8 @@ export class DileInputNumber extends DileInput {
|
|
|
37
37
|
this.decimals = null;
|
|
38
38
|
this.normalizeOn = 'input';
|
|
39
39
|
this.changed = false;
|
|
40
|
+
this.viewValueOnFocus = null;
|
|
41
|
+
this.isInputFocused = false;
|
|
40
42
|
}
|
|
41
43
|
|
|
42
44
|
firstUpdated() {
|
|
@@ -88,14 +90,21 @@ export class DileInputNumber extends DileInput {
|
|
|
88
90
|
return sanitized;
|
|
89
91
|
}
|
|
90
92
|
|
|
93
|
+
normalizeToCanonical(value) {
|
|
94
|
+
return this.sanitizeValue(value, { normalizeSeparator: false }).replace(',', '.');
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
sanitizeForView(value) {
|
|
98
|
+
return this.sanitizeValue(value, { normalizeSeparator: false });
|
|
99
|
+
}
|
|
100
|
+
|
|
91
101
|
normalizeForForm(value) {
|
|
92
102
|
if (!value) {
|
|
93
103
|
return '';
|
|
94
104
|
}
|
|
95
105
|
return value
|
|
96
106
|
.toString()
|
|
97
|
-
.replace(
|
|
98
|
-
.replace(this.alternateDecimalSeparator, '.');
|
|
107
|
+
.replace(',', '.');
|
|
99
108
|
}
|
|
100
109
|
|
|
101
110
|
get normalizeOnBlur() {
|
|
@@ -160,13 +169,13 @@ export class DileInputNumber extends DileInput {
|
|
|
160
169
|
|
|
161
170
|
formatNumber(number) {
|
|
162
171
|
if (this.hasDecimalsConstraint()) {
|
|
163
|
-
return number.toFixed(this.decimals)
|
|
172
|
+
return number.toFixed(this.decimals);
|
|
164
173
|
}
|
|
165
|
-
return number.toString()
|
|
174
|
+
return number.toString();
|
|
166
175
|
}
|
|
167
176
|
|
|
168
177
|
normalizeAndConstrain(value) {
|
|
169
|
-
const sanitized = this.
|
|
178
|
+
const sanitized = this.normalizeToCanonical(value);
|
|
170
179
|
const parsed = this.toNumber(sanitized);
|
|
171
180
|
|
|
172
181
|
if (isNaN(parsed)) {
|
|
@@ -211,13 +220,20 @@ export class DileInputNumber extends DileInput {
|
|
|
211
220
|
}
|
|
212
221
|
|
|
213
222
|
_input(e) {
|
|
214
|
-
const
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
223
|
+
const sanitizedForView = this.sanitizeForView(e.target.value);
|
|
224
|
+
if (sanitizedForView !== e.target.value) {
|
|
225
|
+
e.target.value = sanitizedForView;
|
|
226
|
+
}
|
|
227
|
+
this.viewValueOnFocus = sanitizedForView;
|
|
228
|
+
this.value = this.normalizeToCanonical(sanitizedForView);
|
|
229
|
+
|
|
230
|
+
if (!this.normalizeOnBlur) {
|
|
231
|
+
const formatted = this.computeValue(this.value);
|
|
232
|
+
if (e.target.value !== formatted) {
|
|
233
|
+
e.target.value = formatted;
|
|
234
|
+
this.viewValueOnFocus = formatted;
|
|
235
|
+
}
|
|
219
236
|
}
|
|
220
|
-
this.value = sanitized;
|
|
221
237
|
|
|
222
238
|
if (this.hideErrorOnInput && this.errored) {
|
|
223
239
|
this.clearError();
|
|
@@ -225,6 +241,8 @@ export class DileInputNumber extends DileInput {
|
|
|
225
241
|
}
|
|
226
242
|
|
|
227
243
|
doBlur(e) {
|
|
244
|
+
this.isInputFocused = false;
|
|
245
|
+
this.viewValueOnFocus = null;
|
|
228
246
|
const num = this.normalizeAndConstrain(e.target.value);
|
|
229
247
|
|
|
230
248
|
if (num !== e.target.value || this.changed) {
|
|
@@ -232,6 +250,15 @@ export class DileInputNumber extends DileInput {
|
|
|
232
250
|
this.emmitChange();
|
|
233
251
|
this.changed = false;
|
|
234
252
|
}
|
|
253
|
+
|
|
254
|
+
if (this.normalizeOnBlur) {
|
|
255
|
+
this.el.value = this.computeValue(this.value);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
doFocus() {
|
|
260
|
+
this.isInputFocused = true;
|
|
261
|
+
super.doFocus();
|
|
235
262
|
}
|
|
236
263
|
|
|
237
264
|
updated(changedProperties) {
|
|
@@ -242,14 +269,6 @@ export class DileInputNumber extends DileInput {
|
|
|
242
269
|
return;
|
|
243
270
|
}
|
|
244
271
|
|
|
245
|
-
if (changedProperties.has('decimalSeparator') && this.value) {
|
|
246
|
-
const sanitized = this.sanitizeValue(this.value);
|
|
247
|
-
if (sanitized !== this.value) {
|
|
248
|
-
this.value = sanitized;
|
|
249
|
-
return;
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
|
|
253
272
|
if (changedProperties.has('value') && this.value) {
|
|
254
273
|
this.changed = true;
|
|
255
274
|
}
|
|
@@ -275,8 +294,10 @@ export class DileInputNumber extends DileInput {
|
|
|
275
294
|
}
|
|
276
295
|
|
|
277
296
|
computeValue(value) {
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
}
|
|
297
|
+
if (this.normalizeOnBlur && this.isInputFocused && this.viewValueOnFocus !== null) {
|
|
298
|
+
return this.viewValueOnFocus;
|
|
299
|
+
}
|
|
300
|
+
const canonical = this.normalizeToCanonical(value);
|
|
301
|
+
return canonical.replace('.', this.safeDecimalSeparator);
|
|
281
302
|
}
|
|
282
303
|
}
|
|
@@ -20,11 +20,11 @@ export const DileForm = (superclass) => class extends superclass {
|
|
|
20
20
|
};
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
firstUpdated() {
|
|
23
|
+
async firstUpdated() {
|
|
24
24
|
super.firstUpdated();
|
|
25
25
|
this.firstValue = this.getData();
|
|
26
26
|
if(this.setOnInit) {
|
|
27
|
-
this.setData(this.setOnInit);
|
|
27
|
+
await this.setData(this.setOnInit);
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
30
|
|
|
@@ -32,6 +32,7 @@ export const DileForm = (superclass) => class extends superclass {
|
|
|
32
32
|
let data = {};
|
|
33
33
|
this.allNodeElements.forEach(node => {
|
|
34
34
|
const name = node.getAttribute('name');
|
|
35
|
+
if (!name) return;
|
|
35
36
|
const isArray = name.endsWith('[]');
|
|
36
37
|
const fieldName = isArray ? name.slice(0, -2) : name;
|
|
37
38
|
|
|
@@ -47,16 +48,18 @@ export const DileForm = (superclass) => class extends superclass {
|
|
|
47
48
|
return this.adaptDataOut(data);
|
|
48
49
|
}
|
|
49
50
|
|
|
50
|
-
setData(data) {
|
|
51
|
+
async setData(data) {
|
|
52
|
+
await this.updateComplete;
|
|
51
53
|
const adaptedData = this.adaptDataIn(data);
|
|
52
|
-
this.allNodeElements
|
|
54
|
+
const allNodeElements = this.allNodeElements;
|
|
55
|
+
allNodeElements.forEach(node => {
|
|
53
56
|
const name = node.getAttribute('name');
|
|
57
|
+
if (!name) return;
|
|
54
58
|
const isArray = name.endsWith('[]');
|
|
55
59
|
const fieldName = isArray ? name.slice(0, -2) : name;
|
|
56
60
|
|
|
57
61
|
if (isArray && Array.isArray(adaptedData[fieldName])) {
|
|
58
|
-
|
|
59
|
-
const arrayElements = Array.from(this.allNodeElements).filter(el => el.getAttribute('name') === name);
|
|
62
|
+
const arrayElements = Array.from(allNodeElements).filter(el => el.getAttribute('name') === name);
|
|
60
63
|
const elementIndex = arrayElements.indexOf(node);
|
|
61
64
|
if (elementIndex >= 0 && elementIndex < adaptedData[fieldName].length) {
|
|
62
65
|
const value = adaptedData[fieldName][elementIndex];
|
|
@@ -104,7 +107,7 @@ export const DileForm = (superclass) => class extends superclass {
|
|
|
104
107
|
}
|
|
105
108
|
|
|
106
109
|
resetData() {
|
|
107
|
-
this.setData(this.firstValue);
|
|
110
|
+
return this.setData(this.firstValue);
|
|
108
111
|
}
|
|
109
112
|
|
|
110
113
|
resetField(name) {
|
|
@@ -189,7 +192,7 @@ export const DileForm = (superclass) => class extends superclass {
|
|
|
189
192
|
|
|
190
193
|
isDirty() {
|
|
191
194
|
const currentData = this.getData();
|
|
192
|
-
const originProperties = Object.keys(this.firstValue);
|
|
195
|
+
const originProperties = Object.keys(this.firstValue || {});
|
|
193
196
|
const currentProperties = Object.keys(currentData);
|
|
194
197
|
if (originProperties.length !== currentProperties.length) {
|
|
195
198
|
return true;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { describe, it, expect, afterEach } from 'vitest';
|
|
2
|
+
import { LitElement, html } from 'lit';
|
|
3
|
+
import { DileForm } from './DileForm.js';
|
|
4
|
+
|
|
5
|
+
class RaceTestForm extends DileForm(LitElement) {
|
|
6
|
+
render() {
|
|
7
|
+
return html`<input name="name" />`;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
if (!customElements.get('race-test-form')) {
|
|
12
|
+
customElements.define('race-test-form', RaceTestForm);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
describe('DileForm', () => {
|
|
16
|
+
afterEach(() => {
|
|
17
|
+
document.body.innerHTML = '';
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it('applies setData after the first render is ready even when called immediately', async () => {
|
|
21
|
+
const form = document.createElement('race-test-form');
|
|
22
|
+
document.body.appendChild(form);
|
|
23
|
+
|
|
24
|
+
form.setData({ name: 'Alice' });
|
|
25
|
+
await form.updateComplete;
|
|
26
|
+
|
|
27
|
+
expect(form.shadowRoot.querySelector('input').value).toBe('Alice');
|
|
28
|
+
});
|
|
29
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dile/ui",
|
|
3
|
-
"version": "3.7.
|
|
3
|
+
"version": "3.7.2",
|
|
4
4
|
"description": "UI Core components from dile-components.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -28,5 +28,5 @@
|
|
|
28
28
|
"publishConfig": {
|
|
29
29
|
"access": "public"
|
|
30
30
|
},
|
|
31
|
-
"gitHead": "
|
|
31
|
+
"gitHead": "47effc45eac082504ecaa3554c8af5a8a1055a38"
|
|
32
32
|
}
|