Skip to content

Commit

Permalink
fix(cdk): refactor autofill directive
Browse files Browse the repository at this point in the history
  • Loading branch information
splincode committed May 18, 2022
1 parent e67fd15 commit ac77796
Show file tree
Hide file tree
Showing 16 changed files with 289 additions and 94 deletions.
46 changes: 46 additions & 0 deletions projects/cdk/components/autofilled/autofilled.component.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
@keyframes tuiAutofillStart {
from {
}
}

@keyframes tuiAutofillCancel {
from {
}
}

.tui-autofill {
&,
input {
// @note: need for listen (transitionstart) event
// don't use more than one listener property
transition: box-shadow 0.01s;

&:-webkit-autofill,
&:-webkit-autofill:hover,
&:-webkit-autofill:focus {
animation-duration: 0.01s;
animation-name: tuiAutofillStart;

border-radius: inherit;
border-color: var(--tui-autofill); // @note: fix problem with border colors in safari, firefox

-webkit-text-fill-color: inherit !important;
color: inherit !important;
background-color: var(--tui-autofill) !important;

-webkit-box-shadow: 0 0 0 62.5rem var(--tui-autofill) inset !important; // to overlay native background
}

&:not(:-webkit-autofill) {
animation-duration: 0.01s;
animation-name: tuiAutofillCancel;
}

//noinspection CssInvalidPseudoSelector
&::-webkit-credit-card-auto-fill-button {
pointer-events: none;
background-color: transparent !important;
-webkit-mask-image: none !important;
}
}
}
63 changes: 63 additions & 0 deletions projects/cdk/components/autofilled/autofilled.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import {
ChangeDetectionStrategy,
Component,
ElementRef,
EventEmitter,
HostBinding,
Inject,
Output,
ViewEncapsulation,
} from '@angular/core';
import {TuiDestroyService} from '@taiga-ui/cdk/services';
import {fromEvent} from 'rxjs';
import {filter, takeUntil} from 'rxjs/operators';

@Component({
selector: '[tuiAutofilledChange]',
template: '<ng-content></ng-content>',
styleUrls: ['./autofilled.component.less'],
changeDetection: ChangeDetectionStrategy.OnPush,
encapsulation: ViewEncapsulation.None,
host: {class: 'tui-autofill'},
providers: [TuiDestroyService],
})
export class TuiAutofilledComponent {
@HostBinding('class._autofilled')
autofilled = false;

@Output()
readonly tuiAutofilledChange = new EventEmitter<boolean>();

constructor(
@Inject(ElementRef) private readonly elementRef: ElementRef<Element>,
@Inject(TuiDestroyService) private readonly destroy$: TuiDestroyService,
) {}

get element(): Element {
return this.elementRef.nativeElement;
}

get inputElement(): HTMLInputElement | null {
return this.element?.tagName === 'INPUT'
? (this.element as HTMLInputElement)
: this.element.getElementsByTagName('input')?.[0] || null;
}

ngOnInit(): void {
const input = this.inputElement;

if (input) {
fromEvent<TransitionEvent>(input, 'transitionstart')
.pipe(
filter(event => event.propertyName.includes('box-shadow')),
takeUntil(this.destroy$),
)
.subscribe(() => this.changeAutofill());
}
}

private changeAutofill(): void {
this.autofilled = !this.autofilled;
this.tuiAutofilledChange.emit(this.autofilled);
}
}
9 changes: 9 additions & 0 deletions projects/cdk/components/autofilled/autofilled.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import {NgModule} from '@angular/core';

import {TuiAutofilledComponent} from './autofilled.component';

@NgModule({
declarations: [TuiAutofilledComponent],
exports: [TuiAutofilledComponent],
})
export class TuiAutofilledModule {}
2 changes: 2 additions & 0 deletions projects/cdk/components/autofilled/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './autofilled.component';
export * from './autofilled.module';
165 changes: 165 additions & 0 deletions projects/cdk/components/autofilled/test/autofilled.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
import {Component} from '@angular/core';
import {ComponentFixture, TestBed} from '@angular/core/testing';
import {configureTestSuite, NativeInputPO} from '@taiga-ui/testing';
import {FormsModule} from '@angular/forms';
import {TuiPrimitiveTextfieldModule} from '@taiga-ui/core';
import {TuiAutofilledModule} from '@taiga-ui/cdk';

describe('TuiAutofillModule', () => {
describe('TuiPrimitiveTextfield', () => {
@Component({
template: `
<tui-primitive-textfield
[(value)]="value"
(autofilledChange)="autofilled = $event"
></tui-primitive-textfield>
`,
})
class TestComponent {
value = '';
autofilled = false;
}

let fixture: ComponentFixture<TestComponent>;
let testComponent: TestComponent;
let inputPO: NativeInputPO;

configureTestSuite(() => {
TestBed.configureTestingModule({
imports: [FormsModule, TuiPrimitiveTextfieldModule],
declarations: [TestComponent],
});
});

beforeEach(() => {
fixture = TestBed.createComponent(TestComponent);
testComponent = fixture.componentInstance;
fixture.detectChanges();
inputPO = new NativeInputPO(fixture, `tui-primitive-textfield__native-input`);
});

it('correctly works if `tuiAutofilledChange` is set to `tui-wrapper`', () => {
expect(testComponent.autofilled).toBeFalse();
expect(testComponent.value).toBe('');

expect(getWrapperClassList()).toEqual([
'tui-autofill',
'_no-hover',
'_no-active',
]);

autoFillEvent(inputPO.nativeElement, '1111 2222 3333 4444');
fixture.detectChanges();

expect(getWrapperClassList()).toEqual([
'tui-autofill',
'_no-hover',
'_no-active',
'_autofilled',
]);

expect(testComponent.autofilled).toBeTrue();
expect(testComponent.value).toBe('1111 2222 3333 4444');

autoFillEvent(inputPO.nativeElement);
fixture.detectChanges();

expect(getWrapperClassList()).toEqual([
'tui-autofill',
'_no-hover',
'_no-active',
]);

expect(testComponent.autofilled).toBeFalse();
expect(testComponent.value).toBe('');
});

function getWrapperClassList(): string[] {
return Array.from(
fixture.nativeElement.querySelector('tui-wrapper')?.classList || [],
);
}
});

describe('native input', () => {
@Component({
template: `
<input
id="cardNumber"
type="text"
[(ngModel)]="value"
(tuiAutofilledChange)="autofilled = $event"
/>
`,
})
class TestComponent {
value = '';
autofilled = false;
}

let fixture: ComponentFixture<TestComponent>;
let testComponent: TestComponent;

configureTestSuite(() => {
TestBed.configureTestingModule({
imports: [FormsModule, TuiAutofilledModule],
declarations: [TestComponent],
});
});

beforeEach(() => {
fixture = TestBed.createComponent(TestComponent);
testComponent = fixture.componentInstance;
fixture.detectChanges();
});

it('correctly works if `tuiAutofilledChange` is set to `input`', () => {
expect(testComponent.autofilled).toBeFalse();
expect(testComponent.value).toBe('');
expect(getInputClassList()).toEqual(['tui-autofill']);

autoFillEvent(getInput(), '1111 2222 3333 4444');
fixture.detectChanges();

expect(getInputClassList()).toEqual(['tui-autofill', '_autofilled']);

expect(testComponent.autofilled).toBeTrue();
expect(testComponent.value).toBe('1111 2222 3333 4444');

autoFillEvent(getInput());
fixture.detectChanges();

expect(testComponent.autofilled).toBeFalse();
expect(testComponent.value).toBe('');
expect(getInputClassList()).toEqual(['tui-autofill']);
});

function getInput(): HTMLInputElement {
return fixture.nativeElement?.querySelector('#cardNumber');
}

function getInputClassList(): string[] {
return Array.from(getInput()?.classList || []).filter(
className => !className.includes('ng-'),
);
}
});
});

function triggerTransitionStart(element: Element, propertyName: string): void {
const event = new TransitionEvent('transitionstart', {propertyName});

element.dispatchEvent(event);
}

function triggerInputChange(element: HTMLInputElement, data: string): void {
const event = new InputEvent('input', {data});

element.value = data;
element.dispatchEvent(event);
}

function autoFillEvent(input: HTMLInputElement, data?: string): void {
triggerTransitionStart(input, '-webkit-box-shadow');
triggerInputChange(input, data || '');
}
53 changes: 0 additions & 53 deletions projects/cdk/directives/autofilled/autofilled.directive.ts

This file was deleted.

9 changes: 0 additions & 9 deletions projects/cdk/directives/autofilled/autofilled.module.ts

This file was deleted.

2 changes: 0 additions & 2 deletions projects/cdk/directives/autofilled/index.ts

This file was deleted.

2 changes: 1 addition & 1 deletion projects/cdk/directives/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
export * from '@taiga-ui/cdk/directives/active-zone';
export * from '@taiga-ui/cdk/directives/auto-focus';
export * from '@taiga-ui/cdk/directives/autofilled';
export * from '@taiga-ui/cdk/components/autofilled';
export * from '@taiga-ui/cdk/directives/checked';
export * from '@taiga-ui/cdk/directives/drag';
export * from '@taiga-ui/cdk/directives/droppable';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ export class TuiPrimitiveTextfieldComponent
// TODO: 3.0 Refactor this after we drop built-in input element
return (
(this.focusableElement.nativeElement
.previousElementSibling as HTMLInputElement | null) ||
?.previousElementSibling as HTMLInputElement | null) ||
this.focusableElement.nativeElement
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@
}

.t-input {
text-overflow: ellipsis;

&:not(:first-child) {
display: none;
}
Expand Down
Loading

0 comments on commit ac77796

Please sign in to comment.