Skip to content

Commit e2693f9

Browse files
mkrbreromano
andauthored
AAE-35668 Sending empty string instead of null after clearing value from numeric field (#10925)
* Handles null values in number widget Ensures the number widget correctly handles null, undefined, and empty string values, setting the field value to null in these cases. This change prevents unexpected behavior when the user clears the input field, ensuring data consistency. * Adds test for readonly number widget Adds a test case to verify the behavior of the number widget when it's in readonly mode. The test checks if the displayValue is correctly set using the decimalNumberPipe when the readOnly property is true. * Refactors number widget tests Updates number widget tests to use `overrideComponent` for providing mocked dependencies. This approach ensures proper isolation and avoids potential issues with shared state between tests. Additionally, it adds a test case to verify the `displayValue` is correctly set using the mocked `DecimalNumberPipe`. --------- Co-authored-by: Eugenio Romano <[email protected]>
1 parent 5d9acae commit e2693f9

File tree

3 files changed

+89
-4
lines changed

3 files changed

+89
-4
lines changed

lib/core/src/lib/form/components/widgets/number/number.widget.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
[required]="isRequired()"
2222
[value]="displayValue"
2323
[(ngModel)]="field.value"
24-
(ngModelChange)="onFieldChanged(field)"
24+
(ngModelChange)="onNumberChange($event)"
2525
[disabled]="field.readOnly"
2626
[placeholder]="field.placeholder"
2727
[title]="field.tooltip"

lib/core/src/lib/form/components/widgets/number/number.widget.spec.ts

Lines changed: 80 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,23 +23,100 @@ import { MatInputModule } from '@angular/material/input';
2323
import { CoreTestingModule, UnitTestingUtils } from '../../../../testing';
2424
import { FormFieldModel, FormFieldTypes, FormModel } from '../core';
2525
import { NumberWidgetComponent } from './number.widget';
26+
import { DecimalNumberPipe } from '../../../../pipes';
2627

2728
describe('NumberWidgetComponent', () => {
2829
let loader: HarnessLoader;
2930
let widget: NumberWidgetComponent;
3031
let fixture: ComponentFixture<NumberWidgetComponent>;
3132
let testingUtils: UnitTestingUtils;
33+
let mockDecimalNumberPipe: jasmine.SpyObj<DecimalNumberPipe>;
3234

33-
beforeEach(() => {
34-
TestBed.configureTestingModule({
35+
beforeEach(async () => {
36+
mockDecimalNumberPipe = jasmine.createSpyObj('DecimalNumberPipe', ['transform']);
37+
38+
await TestBed.configureTestingModule({
3539
imports: [CoreTestingModule, MatInputModule, MatIconModule]
36-
});
40+
})
41+
.overrideComponent(NumberWidgetComponent, {
42+
set: {
43+
providers: [{ provide: DecimalNumberPipe, useValue: mockDecimalNumberPipe }]
44+
}
45+
})
46+
.compileComponents();
47+
3748
fixture = TestBed.createComponent(NumberWidgetComponent);
3849
widget = fixture.componentInstance;
3950
loader = TestbedHarnessEnvironment.loader(fixture);
4051
testingUtils = new UnitTestingUtils(fixture.debugElement, loader);
4152
});
4253

54+
it('should create', () => {
55+
expect(widget).toBeTruthy();
56+
});
57+
58+
describe('with readonly true', () => {
59+
beforeEach(() => {
60+
widget.field = new FormFieldModel(new FormModel({ taskId: '<id>' }), {
61+
type: FormFieldTypes.NUMBER,
62+
value: 123.45,
63+
id: 'number-id',
64+
readOnly: true
65+
});
66+
});
67+
68+
it('should set displayValue using decimalNumberPipe', () => {
69+
const expectedValue = '2000';
70+
mockDecimalNumberPipe.transform.and.returnValue(expectedValue);
71+
72+
fixture.detectChanges();
73+
74+
expect(mockDecimalNumberPipe.transform).toHaveBeenCalled();
75+
expect(widget.displayValue).toBe(expectedValue);
76+
});
77+
});
78+
79+
describe('with default value', () => {
80+
beforeEach(() => {
81+
widget.field = new FormFieldModel(new FormModel({ taskId: '<id>' }), {
82+
type: FormFieldTypes.NUMBER,
83+
value: 123,
84+
id: 'number-id',
85+
readOnly: false
86+
});
87+
fixture.detectChanges();
88+
});
89+
90+
it('should display the value', async () => {
91+
const input = await testingUtils.getMatInput();
92+
93+
expect(widget.displayValue).toBe(123);
94+
expect(await input.getValue()).toBe('123');
95+
expect(widget.field.value).toBe(123);
96+
});
97+
98+
it('should have value null when field is cleared', async () => {
99+
const input = await testingUtils.getMatInput();
100+
await input.setValue('');
101+
102+
expect(widget.field.value).toBeNull();
103+
});
104+
105+
it('should have value null when value is undefined', async () => {
106+
const input = await testingUtils.getMatInput();
107+
await input.setValue(undefined);
108+
109+
expect(widget.field.value).toBeNull();
110+
});
111+
112+
it('should have value null when value is null', async () => {
113+
const input = await testingUtils.getMatInput();
114+
await input.setValue(null);
115+
116+
expect(widget.field.value).toBeNull();
117+
});
118+
});
119+
43120
describe('when tooltip is set', () => {
44121
beforeEach(() => {
45122
widget.field = new FormFieldModel(new FormModel({ taskId: '<id>' }), {

lib/core/src/lib/form/components/widgets/number/number.widget.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,4 +62,12 @@ export class NumberWidgetComponent extends WidgetComponent implements OnInit {
6262
this.displayValue = this.field.value;
6363
}
6464
}
65+
66+
protected onNumberChange(value: string) {
67+
if (value === null || value === undefined || value === '') {
68+
this.field.value = null;
69+
}
70+
71+
this.onFieldChanged(this.field);
72+
}
6573
}

0 commit comments

Comments
 (0)