Skip to content

Commit 7498e03

Browse files
fix(mask): deleting items with the igxMask works as expected #11852 (#12302)
1 parent 97d1343 commit 7498e03

File tree

3 files changed

+58
-17
lines changed

3 files changed

+58
-17
lines changed

projects/igniteui-angular/src/lib/directives/mask/mask-parsing.service.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ export interface Replaced {
2020
providedIn: 'root'
2121
})
2222
export class MaskParsingService {
23-
public applyMask(inputVal: string, maskOptions: MaskOptions): string {
23+
public applyMask(inputVal: string, maskOptions: MaskOptions, pos: number = 0): string {
2424
let outputVal = '';
2525
let value = '';
2626
const mask: string = maskOptions.format;
@@ -60,7 +60,6 @@ export class MaskParsingService {
6060
nonLiteralValues.splice(nonLiteralIndices.length);
6161
}
6262

63-
let pos = 0;
6463
for (const nonLiteralValue of nonLiteralValues) {
6564
const char = nonLiteralValue;
6665
outputVal = this.replaceCharAt(outputVal, nonLiteralIndices[pos++], char);
@@ -115,7 +114,13 @@ export class MaskParsingService {
115114
maskedValue = this.replaceCharAt(maskedValue, i, char);
116115
}
117116

118-
return { value: maskedValue, end: cursor };
117+
if (Math.abs(end - start) >= 1) {
118+
// set cursor to be max between last cursor pos and the calculated `end`
119+
// since on `delete` the cursor should move forward
120+
cursor = Math.max(cursor, end);
121+
}
122+
123+
return { value: maskedValue, end: cursor};
119124
}
120125

121126
public replaceCharAt(strValue: string, index: number, char: string): string {

projects/igniteui-angular/src/lib/directives/mask/mask.directive.spec.ts

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ describe('igxMask', () => {
2727
OneWayBindComponent,
2828
PipesMaskComponent,
2929
PlaceholderMaskComponent,
30-
EmptyMaskTestComponent,
30+
MaskTestComponent,
3131
ReadonlyMaskTestComponent
3232
],
3333
imports: [
@@ -378,7 +378,7 @@ describe('igxMask', () => {
378378
}));
379379

380380
it('Should display mask on dragenter and remove it on dragleave', fakeAsync(() => {
381-
const fixture = TestBed.createComponent(EmptyMaskTestComponent);
381+
const fixture = TestBed.createComponent(MaskTestComponent);
382382
fixture.detectChanges();
383383
const input = fixture.componentInstance.input;
384384

@@ -390,6 +390,17 @@ describe('igxMask', () => {
390390

391391
input.nativeElement.dispatchEvent(new DragEvent('dragleave'));
392392
expect(input.nativeElement.value).toEqual('');
393+
394+
// should preserve state on dragenter
395+
input.nativeElement.dispatchEvent(new Event('focus'));
396+
UIInteractions.simulatePaste('76', fixture.debugElement.query(By.css('.igx-input-group__input')), 3, 3);
397+
fixture.detectChanges();
398+
399+
input.nativeElement.dispatchEvent(new Event('blur'));
400+
expect(input.nativeElement.value).toEqual('___76_____');
401+
402+
input.nativeElement.dispatchEvent(new DragEvent('dragenter'));
403+
expect(input.nativeElement.value).toEqual('___76_____');
393404
}));
394405

395406
it('Apply display and input pipes on blur and focus.', fakeAsync(() => {
@@ -485,6 +496,29 @@ describe('igxMask', () => {
485496
expect(fixture.componentInstance.maskDirective.mask).toEqual('##.##');
486497
expect(input.nativeElement.placeholder).toEqual('##.##');
487498
}));
499+
500+
it('should update input properly on selection with DELETE', () => {
501+
const fixture = TestBed.createComponent(MaskComponent);
502+
fixture.detectChanges();
503+
const inputElement = fixture.debugElement.query(By.css('input'));
504+
inputElement.triggerEventHandler('focus');
505+
UIInteractions.simulatePaste('1234567890', inputElement, 1, 1);
506+
fixture.detectChanges();
507+
expect(inputElement.nativeElement.value).toEqual('(123) 4567-890');
508+
509+
const inputHTMLElement = inputElement.nativeElement as HTMLInputElement;
510+
inputHTMLElement.setSelectionRange(6, 8);
511+
fixture.detectChanges();
512+
expect(inputElement.nativeElement.selectionStart).toEqual(6);
513+
expect(inputElement.nativeElement.selectionEnd).toEqual(8);
514+
515+
UIInteractions.triggerEventHandlerKeyDown('Delete', inputElement);
516+
inputElement.triggerEventHandler('input', { inputType: 'test' });
517+
fixture.detectChanges();
518+
expect(inputElement.nativeElement.selectionStart).toEqual(8);
519+
expect(inputElement.nativeElement.selectionEnd).toEqual(8);
520+
expect(inputHTMLElement.value).toEqual('(123) __67-890');
521+
});
488522
});
489523

490524
describe('igxMaskDirective ControlValueAccessor Unit', () => {
@@ -728,7 +762,7 @@ class PipesMaskComponent {
728762
</igx-input-group>
729763
`
730764
})
731-
class EmptyMaskTestComponent {
765+
class MaskTestComponent {
732766
@ViewChild('input', { static: true })
733767
public input: ElementRef;
734768
}

projects/igniteui-angular/src/lib/directives/mask/mask.directive.ts

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -220,17 +220,19 @@ export class IgxMaskDirective implements OnInit, AfterViewChecked, ControlValueA
220220
numberOfMaskLiterals++;
221221
}
222222
}
223-
this.inputValue = isInputComplete?
223+
this.inputValue = isInputComplete ?
224224
this.inputValue.substring(0, this.selectionEnd - numberOfMaskLiterals) + this.inputValue.substring(this.selectionEnd)
225-
: this._compositionValue.substring(0, this._compositionStartIndex);
226-
227-
this._start = this.selectionStart;
228-
this._end = this.selectionEnd;
229-
this.nativeElement.selectionStart = isInputComplete ? this._start - numberOfMaskLiterals : this._compositionStartIndex;
230-
this.nativeElement.selectionEnd = this._end - numberOfMaskLiterals;
231-
this.nativeElement.selectionEnd = this._end;
232-
this._start = this.selectionStart;
233-
this._end = this.selectionEnd;
225+
: this._compositionValue?.substring(0, this._compositionStartIndex) || this.inputValue;
226+
227+
if (this._compositionValue) {
228+
this._start = this.selectionStart;
229+
this._end = this.selectionEnd;
230+
this.nativeElement.selectionStart = isInputComplete ? this._start - numberOfMaskLiterals : this._compositionStartIndex;
231+
this.nativeElement.selectionEnd = this._end - numberOfMaskLiterals;
232+
this.nativeElement.selectionEnd = this._end;
233+
this._start = this.selectionStart;
234+
this._end = this.selectionEnd;
235+
}
234236
}
235237

236238
if (this._hasDropAction) {
@@ -281,7 +283,7 @@ export class IgxMaskDirective implements OnInit, AfterViewChecked, ControlValueA
281283
/** @hidden */
282284
@HostListener('dragenter')
283285
public onDragEnter(): void {
284-
if (!this._focused) {
286+
if (!this._focused && !this._dataValue) {
285287
this.showMask(this._dataValue);
286288
}
287289
}

0 commit comments

Comments
 (0)