Skip to content

Commit

Permalink
feat(lib): allow mask change to trigger onChange (#969)
Browse files Browse the repository at this point in the history
Co-authored-by: Manuel Meister <[email protected]>
  • Loading branch information
manuelmeister and Manuel Meister authored Mar 17, 2022
1 parent 963eb8e commit a99eae2
Show file tree
Hide file tree
Showing 13 changed files with 247 additions and 53 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
<a name="13.1.2"></a>

# 13.1.2 (2022-01-20)

### Features

- added triggerOnMaskChange, allow mask change to trigger onChange

<a name="13.1.1"></a>

# 13.1.1 (2022-01-12)
Expand Down
2 changes: 2 additions & 0 deletions projects/ngx-mask-lib/src/lib/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export interface IConfig {
separatorLimit: string;
allowNegativeNumbers: boolean;
leadZeroDateTime: boolean;
triggerOnMaskChange: boolean;
patterns: {
[character: string]: {
pattern: RegExp;
Expand Down Expand Up @@ -49,6 +50,7 @@ export const initialConfig: IConfig = {
// eslint-disable-next-line @typescript-eslint/quotes
specialCharacters: ['-', '/', '(', ')', '.', ':', ' ', '+', ',', '@', '[', ']', '"', "'"],
leadZeroDateTime: false,
triggerOnMaskChange: false,
patterns: {
'0': {
pattern: new RegExp('\\d'),
Expand Down
6 changes: 6 additions & 0 deletions projects/ngx-mask-lib/src/lib/mask.directive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ export class MaskDirective implements ControlValueAccessor, OnChanges, Validator

@Input() public leadZeroDateTime: IConfig['leadZeroDateTime'] | null = null;

@Input() public triggerOnMaskChange: IConfig['triggerOnMaskChange'] | null = null;

private _maskValue: string = '';

private _inputValue!: string;
Expand Down Expand Up @@ -121,6 +123,7 @@ export class MaskDirective implements ControlValueAccessor, OnChanges, Validator
separatorLimit,
allowNegativeNumbers,
leadZeroDateTime,
triggerOnMaskChange,
} = changes;
if (maskExpression) {
if (
Expand Down Expand Up @@ -205,6 +208,9 @@ export class MaskDirective implements ControlValueAccessor, OnChanges, Validator
if (leadZeroDateTime) {
this._maskService.leadZeroDateTime = leadZeroDateTime.currentValue;
}
if (triggerOnMaskChange) {
this._maskService.triggerOnMaskChange = triggerOnMaskChange.currentValue;
}
this._applyMask();
}

Expand Down
4 changes: 3 additions & 1 deletion projects/ngx-mask-lib/src/lib/mask.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ export class MaskService extends MaskApplierService {

public maskChanged: boolean = false;

public triggerOnMaskChange: boolean = false;

public onChange = (_: any) => {};

public constructor(
Expand Down Expand Up @@ -352,7 +354,7 @@ export class MaskService extends MaskApplierService {
* @param inputValue the current form input value
*/
private formControlResult(inputValue: string): void {
if (this.writingValue || this.maskChanged) {
if (this.writingValue || (!this.triggerOnMaskChange && this.maskChanged)) {
this.maskChanged = false;
return;
}
Expand Down
71 changes: 32 additions & 39 deletions projects/ngx-mask-lib/src/test/delete.cy-spec.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import { mount } from '@jscutlery/cypress-angular/mount';
import {
CypressTestMaskComponent,
CypressTestMaskModule,
} from './utils/cypress-test-component.component';
import { CypressTestMaskComponent } from './utils/cypress-test-component.component';
import { CypressTestMaskModule } from './utils/cypress-test.module';

describe('Directive: Mask (Delete)', () => {
it('should delete character in input', () => {
Expand All @@ -13,13 +11,12 @@ describe('Directive: Mask (Delete)', () => {
imports: [CypressTestMaskModule],
});

const inputTarget = cy.get('input');
inputTarget.type('12/34/5678');
inputTarget.focus();
inputTarget.setSelectionRange(1, 1);
inputTarget.type('{backspace}');

inputTarget.should('have.value', '23/45/678');
cy.get('input#masked')
.type('12/34/5678')
.focus()
.setSelectionRange(1, 1)
.type('{backspace}')
.should('have.value', '23/45/678');
});

it('should not delete special mask character', () => {
Expand All @@ -30,13 +27,12 @@ describe('Directive: Mask (Delete)', () => {
imports: [CypressTestMaskModule],
});

const inputTarget = cy.get('input');
inputTarget.type('12/34/5678');
inputTarget.setSelectionRange(3, 3);
inputTarget.type('{backspace}');

inputTarget.should('have.value', '12/34/5678');
inputTarget.should('have.prop', 'selectionStart', 2);
cy.get('input#masked')
.type('12/34/5678')
.setSelectionRange(3, 3)
.type('{backspace}')
.should('have.value', '12/34/5678')
.should('have.prop', 'selectionStart', 2);
});

it('should delete secure character', () => {
Expand All @@ -48,13 +44,12 @@ describe('Directive: Mask (Delete)', () => {
imports: [CypressTestMaskModule],
});

const inputTarget = cy.get('input');
inputTarget.type('123/45/6789');
inputTarget.setSelectionRange(3, 3);
inputTarget.type('{backspace}');

inputTarget.should('have.value', '***/*6/789');
inputTarget.should('have.prop', 'selectionStart', 2);
cy.get('input#masked')
.type('123/45/6789')
.setSelectionRange(3, 3)
.type('{backspace}')
.should('have.value', '***/*6/789')
.should('have.prop', 'selectionStart', 2);
});

it('should not delete prefix', () => {
Expand All @@ -66,13 +61,12 @@ describe('Directive: Mask (Delete)', () => {
imports: [CypressTestMaskModule],
});

const inputTarget = cy.get('input');
inputTarget.type('1234');
inputTarget.setSelectionRange(3, 3);
inputTarget.type('{backspace}');

inputTarget.should('have.value', '+1 (12) 34');
inputTarget.should('have.prop', 'selectionStart', 3);
cy.get('input#masked')
.type('1234')
.setSelectionRange(3, 3)
.type('{backspace}')
.should('have.value', '+1 (12) 34')
.should('have.prop', 'selectionStart', 3);
});

it('should delete selection', () => {
Expand All @@ -83,12 +77,11 @@ describe('Directive: Mask (Delete)', () => {
imports: [CypressTestMaskModule],
});

const inputTarget = cy.get('input');
inputTarget.type('123456789');
inputTarget.setSelectionRange(4, 7);
inputTarget.type('{backspace}');

inputTarget.should('have.value', '123 789');
inputTarget.should('have.prop', 'selectionStart', 3);
cy.get('input#masked')
.type('123456789')
.setSelectionRange(4, 7)
.type('{backspace}')
.should('have.value', '123 789')
.should('have.prop', 'selectionStart', 3);
});
});
23 changes: 23 additions & 0 deletions projects/ngx-mask-lib/src/test/trigger-on-mask-change.cy-spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { mount } from '@jscutlery/cypress-angular/mount';

import { CypressTestTriggerOnMaskChangeComponent } from './utils/cypress-test-trigger-on-mask-change.component';
import { CypressTestMaskModule } from './utils/cypress-test.module';

describe('Directive: Mask (Trigger on mask change) [Cypress]', () => {
it('should put back initial value if mask is toggled', async () => {
mount(CypressTestTriggerOnMaskChangeComponent, {
imports: [CypressTestMaskModule],
});

cy.get('input#masked').type('7912345678').should('have.value', '7912345678');
cy.get('.formvalue').should('have.text', '7912345678');

cy.get('input[value="ch"]').click();
cy.get('input#masked').should('have.value', '79 123 45 67');
cy.get('.formvalue').should('have.text', '791234567');

cy.get('input[value="de"]').click();
cy.get('input#masked').should('have.value', '7912345678');
cy.get('.formvalue').should('have.text', '7912345678');
});
});
45 changes: 45 additions & 0 deletions projects/ngx-mask-lib/src/test/trigger-on-mask-change.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';

import { By } from '@angular/platform-browser';
import { NgxMaskModule } from '../lib/ngx-mask.module';
import { ReactiveFormsModule } from '@angular/forms';
import { TestMaskComponent } from './utils/test-component.component';

describe('Directive: Mask (Trigger on mask change)', () => {
let fixture: ComponentFixture<TestMaskComponent>;
let component: TestMaskComponent;

beforeEach(() => {
TestBed.configureTestingModule({
declarations: [TestMaskComponent],
imports: [ReactiveFormsModule, NgxMaskModule.forRoot()],
});
fixture = TestBed.createComponent(TestMaskComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});

afterEach(() => {
fixture.destroy();
});

it('should trigger form value update if mask is changed', async () => {
component.mask = '';
component.triggerOnMaskChange = true;
fixture.detectChanges();

component.form.setValue('7912345678');
fixture.detectChanges();
await fixture.whenStable();
let inputEl = fixture.debugElement.query(By.css('input'));
expect(inputEl.nativeElement.value).toEqual('7912345678');
expect(component.form.value).toEqual('7912345678');

component.mask = '00 000 00 00';
fixture.detectChanges();
await fixture.whenStable();
inputEl = fixture.debugElement.query(By.css('input'));
expect(inputEl.nativeElement.value).toEqual('79 123 45 67');
expect(component.form.value).toEqual('791234567');
});
});
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
import { Component, Input, NgModule } from '@angular/core';
import { FormControl, FormsModule, ReactiveFormsModule } from '@angular/forms';

import { CommonModule } from '@angular/common';
import { NgxMaskModule } from 'ngx-mask';
import { Component, Input } from '@angular/core';
import { FormControl } from '@angular/forms';

@Component({
selector: 'mask-cypress-test-mask',
template: `
<input
id="maska"
id="masked"
[formControl]="form"
[mask]="mask"
[hiddenInput]="hiddenInput"
Expand All @@ -25,10 +22,3 @@ export class CypressTestMaskComponent {

public form: FormControl = new FormControl('');
}

@NgModule({
imports: [CommonModule, ReactiveFormsModule, FormsModule, NgxMaskModule.forRoot()],
declarations: [CypressTestMaskComponent],
exports: [CypressTestMaskComponent],
})
export class CypressTestMaskModule {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { Component, OnDestroy, OnInit } from '@angular/core';
import { FormControl } from '@angular/forms';
import { Subject, takeUntil } from 'rxjs';

@Component({
selector: 'mask-cypress-test-mask',
styles: [
'code { border: 1px solid #ddd; background-color: #eee; padding: 0 5px; border-radius: 3px; }',
],
template: `
<div>
<label>
<input type="radio" [formControl]="radio" value="de" />
DE
</label>
<label>
<input type="radio" [formControl]="radio" value="ch" />
CH
</label>
<input
id="masked"
[formControl]="form"
[mask]="mask"
[hiddenInput]="false"
[triggerOnMaskChange]="true"
prefix=""
/>
</div>
<div>
<span>Mask:&nbsp;</span><code class="mask">{{ mask }}</code>
<br />
<span>Form Value:&nbsp;</span><code class="formvalue">{{ form.value }}</code>
</div>
`,
})
export class CypressTestTriggerOnMaskChangeComponent implements OnInit, OnDestroy {
public mask: string = '';

public form: FormControl = new FormControl('');

public radio: FormControl = new FormControl('de');

private destroyed = new Subject<void>();

public ngOnInit(): void {
this.radio.valueChanges.pipe(takeUntil(this.destroyed)).subscribe((value) => {
this.mask = value === 'de' ? '' : '00 000 00 00';
});
}

public ngOnDestroy(): void {
this.destroyed.next();
this.destroyed.complete();
}
}
13 changes: 13 additions & 0 deletions projects/ngx-mask-lib/src/test/utils/cypress-test.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { NgxMaskModule } from 'ngx-mask';
import { CypressTestTriggerOnMaskChangeComponent } from './cypress-test-trigger-on-mask-change.component';
import { CypressTestMaskComponent } from './cypress-test-component.component';

@NgModule({
imports: [CommonModule, ReactiveFormsModule, FormsModule, NgxMaskModule.forRoot()],
declarations: [CypressTestMaskComponent, CypressTestTriggerOnMaskChangeComponent],
exports: [CypressTestMaskComponent, CypressTestTriggerOnMaskChangeComponent],
})
export class CypressTestMaskModule {}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import { IConfig } from '../../lib/config';
[hiddenInput]="hiddenInput"
[allowNegativeNumbers]="allowNegativeNumbers"
[leadZeroDateTime]="leadZeroDateTime"
[triggerOnMaskChange]="triggerOnMaskChange"
/>
`,
})
Expand Down Expand Up @@ -59,4 +60,6 @@ export class TestMaskComponent {
public allowNegativeNumbers: IConfig['allowNegativeNumbers'] = false;

public leadZeroDateTime: IConfig['leadZeroDateTime'] = false;

public triggerOnMaskChange: IConfig['triggerOnMaskChange'] = false;
}
Loading

0 comments on commit a99eae2

Please sign in to comment.