Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(angular): add CVA and pipe #187

Merged
merged 13 commits into from
Mar 15, 2023
Merged
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
uses: ./.github/actions/nodejs

- name: Run tests
run: npx nx run-many --target test --all --coverage
run: npx nx run-many --target test --all

- uses: codecov/codecov-action@v2
with:
Expand Down
2 changes: 2 additions & 0 deletions projects/angular/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
export * from './lib/maskito.cva';
export * from './lib/maskito.directive';
export * from './lib/maskito.module';
export * from './lib/maskito.pipe';
28 changes: 28 additions & 0 deletions projects/angular/src/lib/maskito.cva.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import {Directive, Input} from '@angular/core';
import {DefaultValueAccessor, NG_VALUE_ACCESSOR} from '@angular/forms';
import {MASKITO_DEFAULT_OPTIONS, MaskitoOptions, maskitoTransform} from '@maskito/core';

@Directive({
selector: 'input[maskito], textarea[maskito]',
providers: [
{
provide: NG_VALUE_ACCESSOR,
multi: true,
useExisting: MaskitoCva,
},
],
host: {
'(input)': '$any(this)._handleInput($event.target.value)',
'(blur)': 'onTouched()',
'(compositionstart)': '$any(this)._compositionStart()',
'(compositionend)': '$any(this)._compositionEnd($event.target.value)',
},
})
export class MaskitoCva extends DefaultValueAccessor {
@Input()
maskito: MaskitoOptions = MASKITO_DEFAULT_OPTIONS;

override writeValue(value: unknown): void {
super.writeValue(maskitoTransform(String(value ?? ''), this.maskito));
nsbarsukov marked this conversation as resolved.
Show resolved Hide resolved
}
}
6 changes: 4 additions & 2 deletions projects/angular/src/lib/maskito.module.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import {NgModule} from '@angular/core';

import {MaskitoCva} from './maskito.cva';
import {MaskitoDirective} from './maskito.directive';
import {MaskitoPipe} from './maskito.pipe';

@NgModule({
declarations: [MaskitoDirective],
exports: [MaskitoDirective],
declarations: [MaskitoDirective, MaskitoCva, MaskitoPipe],
exports: [MaskitoDirective, MaskitoCva, MaskitoPipe],
})
export class MaskitoModule {}
11 changes: 11 additions & 0 deletions projects/angular/src/lib/maskito.pipe.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import {Pipe, PipeTransform} from '@angular/core';
import {MaskitoOptions, maskitoTransform} from '@maskito/core';

@Pipe({
name: 'maskito',
})
export class MaskitoPipe implements PipeTransform {
transform(value: unknown, maskitoOptions: MaskitoOptions): string {
return maskitoTransform(String(value ?? ''), maskitoOptions);
}
}
57 changes: 57 additions & 0 deletions projects/angular/src/lib/maskito.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import {Component} from '@angular/core';
import {ComponentFixture, TestBed} from '@angular/core/testing';
import {FormControl, ReactiveFormsModule} from '@angular/forms';
import {MaskitoModule} from '@maskito/angular';
import {maskitoNumberOptionsGenerator} from '@maskito/kit';

describe(`Maskito Angular package`, () => {
@Component({
template: `
<div id="pipe">{{ control.value | maskito: options }}</div>
<input
id="input"
[formControl]="control"
[maskito]="options"
/>
`,
})
class TestComponent {
readonly control = new FormControl();
readonly options = maskitoNumberOptionsGenerator({precision: 2});
}

let fixture: ComponentFixture<TestComponent>;

beforeEach(() => {
TestBed.configureTestingModule({
imports: [MaskitoModule, ReactiveFormsModule],
declarations: [TestComponent],
});

fixture = TestBed.createComponent(TestComponent);
fixture.detectChanges();
});

it(`Null is treated as empty string`, () => {
expect(getText()).toBe(``);
expect(getValue()).toBe(``);
});

it(`Formats new control value`, () => {
fixture.componentInstance.control.setValue(12345.67);
fixture.detectChanges();

expect(getText()).toBe(`12\u00A0345.67`);
expect(getValue()).toBe(`12\u00A0345.67`);
});

function getText(): string {
return fixture.debugElement.nativeElement
.querySelector('#pipe')
.textContent.trim();
}

function getValue(): string {
return fixture.debugElement.nativeElement.querySelector('#input').value;
}
});
Original file line number Diff line number Diff line change
Expand Up @@ -107,17 +107,17 @@ describe('Number | Basic', () => {
it('can type "–123.45" (en-dash)', () => {
cy.get('@input')
.type('–123.45')
.should('have.value', '−123,45')
.should('have.prop', 'selectionStart', '−123,45'.length)
.should('have.prop', 'selectionEnd', '−123,45'.length);
.should('have.value', '−123.45')
.should('have.prop', 'selectionStart', '−123.45'.length)
.should('have.prop', 'selectionEnd', '−123.45'.length);
});

it('can type "—0,12" (em-dash)', () => {
cy.get('@input')
.type('—0,12')
.should('have.value', '−0,12')
.should('have.prop', 'selectionStart', '−0,12'.length)
.should('have.prop', 'selectionEnd', '−0,12'.length);
.should('have.value', '−0.12')
.should('have.prop', 'selectionStart', '−0.12'.length)
.should('have.prop', 'selectionEnd', '−0.12'.length);
});
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,17 @@ describe('Number | Max validation', () => {
it('0,9999', () => {
cy.get('@input')
.type(',9999')
.should('have.value', '0,9999')
.should('have.prop', 'selectionStart', '0,9999'.length)
.should('have.prop', 'selectionEnd', '0,9999'.length);
.should('have.value', '0.9999')
.should('have.prop', 'selectionStart', '0.9999'.length)
.should('have.prop', 'selectionEnd', '0.9999'.length);
});

it('2,777', () => {
cy.get('@input')
.type('2,777')
.should('have.value', '2,777')
.should('have.prop', 'selectionStart', '2,777'.length)
.should('have.prop', 'selectionEnd', '2,777'.length);
.should('have.value', '2.777')
.should('have.prop', 'selectionStart', '2.777'.length)
.should('have.prop', 'selectionEnd', '2.777'.length);
});
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,14 @@ export class AngularDocPageComponent {
Default: import('./examples/1-nested/template.html?raw'),
Custom: import('./examples/2-nested/template.html?raw'),
};

readonly cvaExample: TuiDocExample = {
TypeScript: import('./examples/3-cva/component.ts?raw'),
HTML: import('./examples/3-cva/template.html?raw'),
};

readonly pipeExample: TuiDocExample = {
TypeScript: import('./examples/4-pipe/component.ts?raw'),
HTML: import('./examples/4-pipe/template.html?raw'),
};
}
15 changes: 12 additions & 3 deletions projects/demo/src/pages/documentation/angular/angular.module.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {CommonModule} from '@angular/common';
import {NgModule} from '@angular/core';
import {FormsModule} from '@angular/forms';
import {FormsModule, ReactiveFormsModule} from '@angular/forms';
import {RouterModule} from '@angular/router';
import {MaskitoModule} from '@maskito/angular';
import {TuiAddonDocModule, tuiGenerateRoutes} from '@taiga-ui/addon-doc';
Expand All @@ -10,20 +10,29 @@ import {TuiCheckboxLabeledModule, TuiInputModule} from '@taiga-ui/kit';
import {AngularDocPageComponent} from './angular.component';
import {NestedDocExample1} from './examples/1-nested/component';
import {NestedDocExample2} from './examples/2-nested/component';
import {CvaDocExample3} from './examples/3-cva/component';
import {PipeDocExample4} from './examples/4-pipe/component';

@NgModule({
imports: [
CommonModule,
FormsModule,
ReactiveFormsModule,
MaskitoModule,
TuiInputModule,
TuiLinkModule,
TuiNotificationModule,
TuiCheckboxLabeledModule,
TuiAddonDocModule,
RouterModule.forChild(tuiGenerateRoutes(AngularDocPageComponent)),
TuiCheckboxLabeledModule,
],
declarations: [AngularDocPageComponent, NestedDocExample1, NestedDocExample2],
declarations: [
AngularDocPageComponent,
NestedDocExample1,
NestedDocExample2,
CvaDocExample3,
PipeDocExample4,
],
exports: [AngularDocPageComponent],
})
export class AngularDocPageModule {}
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,22 @@ <h2>Nested input element</h2>
[maskitoElement]="example.predicate"
></nested-doc-example-2>
</tui-doc-example>

<tui-doc-example
heading="Form controls"
description="When directly on native input/textarea tag, maskito directive formats value set programmatically with Angular forms."
[content]="cvaExample"
>
<cva-doc-example-3></cva-doc-example-3>
</tui-doc-example>

<tui-doc-example
heading="Pipe"
description="Format arbitrary value with the same options"
[content]="pipeExample"
>
<pipe-doc-example-4></pipe-doc-example-4>
</tui-doc-example>
</ng-template>

<ng-template pageTab="Setup">
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import {ChangeDetectionStrategy, Component} from '@angular/core';
import {FormControl} from '@angular/forms';
import {maskitoNumberOptionsGenerator} from '@maskito/kit';

@Component({
selector: 'cva-doc-example-3',
templateUrl: './template.html',
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class CvaDocExample3 {
readonly control = new FormControl();

readonly maskito = maskitoNumberOptionsGenerator({precision: 2});

setValue(): void {
this.control.setValue(12345.67);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<input
[formControl]="control"
[maskito]="maskito"
/>
<button (click)="setValue()">Set 12345.67</button>
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import {ChangeDetectionStrategy, Component} from '@angular/core';
import {maskitoNumberOptionsGenerator} from '@maskito/kit';

@Component({
selector: 'pipe-doc-example-4',
templateUrl: './template.html',
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class PipeDocExample4 {
value = 12345.67;

readonly options = maskitoNumberOptionsGenerator({precision: 2});
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Balance: ${{ value | maskito: options }}
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ export class NumberMaskDocComponent implements GeneratorOptions {
precision = 0;
isNegativeAllowed = true;
max = Number.MAX_SAFE_INTEGER;
decimalSeparator = ',';
decimalSeparator = '.';
decimalZeroPadding = false;
decimalPseudoSeparators = this.decimalPseudoSeparatorsOptions[0];
thousandSeparator = ' ';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@

<p>
<strong>Default:</strong>
comma.
dot.
</p>
</ng-template>
<ng-template
Expand Down
2 changes: 1 addition & 1 deletion projects/kit/src/lib/masks/number/number-mask.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export function maskitoNumberOptionsGenerator({
isNegativeAllowed = true,
precision = 0,
thousandSeparator = CHAR_NO_BREAK_SPACE,
decimalSeparator = ',',
decimalSeparator = '.',
waterplea marked this conversation as resolved.
Show resolved Hide resolved
nsbarsukov marked this conversation as resolved.
Show resolved Hide resolved
decimalPseudoSeparators = getDefaultPseudoSeparators({
decimalSeparator,
thousandSeparator,
Expand Down