294 lines
9.6 KiB
TypeScript
294 lines
9.6 KiB
TypeScript
import {Directionality} from '@angular/cdk/bidi';
|
|
import {COMMA, ENTER, TAB} from '@angular/cdk/keycodes';
|
|
import {PlatformModule} from '@angular/cdk/platform';
|
|
import {
|
|
createKeyboardEvent,
|
|
dispatchKeyboardEvent,
|
|
dispatchEvent,
|
|
} from '@angular/cdk/testing/private';
|
|
import {Component, DebugElement, ViewChild} from '@angular/core';
|
|
import {ComponentFixture, TestBed, fakeAsync, flush, waitForAsync} from '@angular/core/testing';
|
|
import {MatFormFieldModule} from '@angular/material/form-field';
|
|
import {By} from '@angular/platform-browser';
|
|
import {NoopAnimationsModule} from '@angular/platform-browser/animations';
|
|
import {Subject} from 'rxjs';
|
|
import {
|
|
MAT_CHIPS_DEFAULT_OPTIONS,
|
|
MatChipGrid,
|
|
MatChipInput,
|
|
MatChipInputEvent,
|
|
MatChipsDefaultOptions,
|
|
MatChipsModule,
|
|
} from './index';
|
|
|
|
describe('MatChipInput', () => {
|
|
let fixture: ComponentFixture<any>;
|
|
let testChipInput: TestChipInput;
|
|
let inputDebugElement: DebugElement;
|
|
let inputNativeElement: HTMLElement;
|
|
let chipInputDirective: MatChipInput;
|
|
let dir = 'ltr';
|
|
|
|
beforeEach(waitForAsync(() => {
|
|
TestBed.configureTestingModule({
|
|
imports: [PlatformModule, MatChipsModule, MatFormFieldModule, NoopAnimationsModule],
|
|
providers: [
|
|
{
|
|
provide: Directionality,
|
|
useFactory: () => {
|
|
return {
|
|
value: dir.toLowerCase(),
|
|
change: new Subject(),
|
|
};
|
|
},
|
|
},
|
|
],
|
|
declarations: [TestChipInput],
|
|
});
|
|
}));
|
|
|
|
beforeEach(waitForAsync(() => {
|
|
fixture = TestBed.createComponent(TestChipInput);
|
|
testChipInput = fixture.debugElement.componentInstance;
|
|
fixture.detectChanges();
|
|
|
|
inputDebugElement = fixture.debugElement.query(By.directive(MatChipInput))!;
|
|
chipInputDirective = inputDebugElement.injector.get<MatChipInput>(MatChipInput);
|
|
inputNativeElement = inputDebugElement.nativeElement;
|
|
}));
|
|
|
|
describe('basic behavior', () => {
|
|
it('emits the (chipEnd) on enter keyup', () => {
|
|
spyOn(testChipInput, 'add');
|
|
|
|
dispatchKeyboardEvent(inputNativeElement, 'keydown', ENTER);
|
|
expect(testChipInput.add).toHaveBeenCalled();
|
|
});
|
|
|
|
it('should have a default id', () => {
|
|
expect(inputNativeElement.getAttribute('id')).toBeTruthy();
|
|
});
|
|
|
|
it('should allow binding to the `placeholder` input', () => {
|
|
expect(inputNativeElement.hasAttribute('placeholder')).toBe(false);
|
|
|
|
testChipInput.placeholder = 'bound placeholder';
|
|
fixture.changeDetectorRef.markForCheck();
|
|
fixture.detectChanges();
|
|
|
|
expect(inputNativeElement.getAttribute('placeholder')).toBe('bound placeholder');
|
|
});
|
|
|
|
it('should become disabled if the list is disabled', () => {
|
|
expect(inputNativeElement.hasAttribute('disabled')).toBe(false);
|
|
expect(chipInputDirective.disabled).toBe(false);
|
|
|
|
fixture.componentInstance.chipGridInstance.disabled = true;
|
|
fixture.changeDetectorRef.markForCheck();
|
|
fixture.detectChanges();
|
|
|
|
expect(inputNativeElement.getAttribute('disabled')).toBe('true');
|
|
expect(chipInputDirective.disabled).toBe(true);
|
|
});
|
|
|
|
it('should be aria-required if the list is required', () => {
|
|
expect(inputNativeElement.hasAttribute('aria-required')).toBe(false);
|
|
|
|
fixture.componentInstance.required = true;
|
|
fixture.changeDetectorRef.markForCheck();
|
|
fixture.detectChanges();
|
|
|
|
expect(inputNativeElement.getAttribute('aria-required')).toBe('true');
|
|
});
|
|
|
|
it('should be required if the list is required', () => {
|
|
expect(inputNativeElement.hasAttribute('required')).toBe(false);
|
|
|
|
fixture.componentInstance.required = true;
|
|
fixture.changeDetectorRef.markForCheck();
|
|
fixture.detectChanges();
|
|
|
|
expect(inputNativeElement.getAttribute('required')).toBe('true');
|
|
});
|
|
|
|
it('should allow focus to escape when tabbing forwards', fakeAsync(() => {
|
|
const gridElement: HTMLElement = fixture.nativeElement.querySelector('mat-chip-grid');
|
|
|
|
expect(gridElement.getAttribute('tabindex')).toBe('0');
|
|
|
|
dispatchKeyboardEvent(gridElement, 'keydown', TAB);
|
|
fixture.detectChanges();
|
|
|
|
expect(gridElement.getAttribute('tabindex'))
|
|
.withContext('Expected tabIndex to be set to -1 temporarily.')
|
|
.toBe('-1');
|
|
|
|
flush();
|
|
fixture.detectChanges();
|
|
|
|
expect(gridElement.getAttribute('tabindex'))
|
|
.withContext('Expected tabIndex to be reset back to 0')
|
|
.toBe('0');
|
|
}));
|
|
|
|
it('should set input styling classes', () => {
|
|
expect(inputNativeElement.classList).toContain('mat-mdc-input-element');
|
|
expect(inputNativeElement.classList).toContain('mat-mdc-form-field-input-control');
|
|
expect(inputNativeElement.classList).toContain('mat-mdc-chip-input');
|
|
expect(inputNativeElement.classList).toContain('mdc-text-field__input');
|
|
});
|
|
});
|
|
|
|
describe('[addOnBlur]', () => {
|
|
it('allows (chipEnd) when true', () => {
|
|
spyOn(testChipInput, 'add');
|
|
|
|
testChipInput.addOnBlur = true;
|
|
fixture.changeDetectorRef.markForCheck();
|
|
fixture.detectChanges();
|
|
|
|
chipInputDirective._blur();
|
|
expect(testChipInput.add).toHaveBeenCalled();
|
|
});
|
|
|
|
it('disallows (chipEnd) when false', () => {
|
|
spyOn(testChipInput, 'add');
|
|
|
|
testChipInput.addOnBlur = false;
|
|
fixture.changeDetectorRef.markForCheck();
|
|
fixture.detectChanges();
|
|
|
|
chipInputDirective._blur();
|
|
expect(testChipInput.add).not.toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
describe('[separatorKeyCodes]', () => {
|
|
it('does not emit (chipEnd) when a non-separator key is pressed', () => {
|
|
spyOn(testChipInput, 'add');
|
|
|
|
chipInputDirective.separatorKeyCodes = [COMMA];
|
|
fixture.detectChanges();
|
|
|
|
dispatchKeyboardEvent(inputNativeElement, 'keydown', ENTER);
|
|
expect(testChipInput.add).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('emits (chipEnd) when a custom separator keys is pressed', () => {
|
|
spyOn(testChipInput, 'add');
|
|
|
|
chipInputDirective.separatorKeyCodes = [COMMA];
|
|
fixture.detectChanges();
|
|
|
|
dispatchKeyboardEvent(inputNativeElement, 'keydown', COMMA);
|
|
expect(testChipInput.add).toHaveBeenCalled();
|
|
});
|
|
|
|
it('emits accepts the custom separator keys in a Set', () => {
|
|
spyOn(testChipInput, 'add');
|
|
|
|
chipInputDirective.separatorKeyCodes = new Set([COMMA]);
|
|
fixture.detectChanges();
|
|
|
|
dispatchKeyboardEvent(inputNativeElement, 'keydown', COMMA);
|
|
expect(testChipInput.add).toHaveBeenCalled();
|
|
});
|
|
|
|
it('emits (chipEnd) when the separator keys are configured globally', () => {
|
|
fixture.destroy();
|
|
|
|
TestBed.resetTestingModule().configureTestingModule({
|
|
imports: [MatChipsModule, MatFormFieldModule, PlatformModule, NoopAnimationsModule],
|
|
declarations: [TestChipInput],
|
|
providers: [
|
|
{
|
|
provide: MAT_CHIPS_DEFAULT_OPTIONS,
|
|
useValue: {separatorKeyCodes: [COMMA]} as MatChipsDefaultOptions,
|
|
},
|
|
],
|
|
});
|
|
|
|
fixture = TestBed.createComponent(TestChipInput);
|
|
testChipInput = fixture.debugElement.componentInstance;
|
|
fixture.detectChanges();
|
|
|
|
inputDebugElement = fixture.debugElement.query(By.directive(MatChipInput))!;
|
|
chipInputDirective = inputDebugElement.injector.get<MatChipInput>(MatChipInput);
|
|
inputNativeElement = inputDebugElement.nativeElement;
|
|
|
|
spyOn(testChipInput, 'add');
|
|
fixture.detectChanges();
|
|
|
|
dispatchKeyboardEvent(inputNativeElement, 'keydown', COMMA);
|
|
expect(testChipInput.add).toHaveBeenCalled();
|
|
});
|
|
|
|
it('should not emit the chipEnd event if a separator is pressed with a modifier key', () => {
|
|
spyOn(testChipInput, 'add');
|
|
|
|
chipInputDirective.separatorKeyCodes = [ENTER];
|
|
fixture.detectChanges();
|
|
|
|
dispatchKeyboardEvent(inputNativeElement, 'keydown', ENTER, undefined, {shift: true});
|
|
expect(testChipInput.add).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('should set aria-describedby correctly when a non-empty list of ids is passed to setDescribedByIds', fakeAsync(() => {
|
|
const ids = ['a', 'b', 'c'];
|
|
|
|
testChipInput.chipGridInstance.setDescribedByIds(ids);
|
|
flush();
|
|
fixture.detectChanges();
|
|
|
|
expect(inputNativeElement.getAttribute('aria-describedby')).toEqual('a b c');
|
|
}));
|
|
|
|
it('should set aria-describedby correctly when an empty list of ids is passed to setDescribedByIds', fakeAsync(() => {
|
|
const ids: string[] = [];
|
|
|
|
testChipInput.chipGridInstance.setDescribedByIds(ids);
|
|
flush();
|
|
fixture.detectChanges();
|
|
|
|
expect(inputNativeElement.getAttribute('aria-describedby')).toBeNull();
|
|
}));
|
|
|
|
it('should not emit chipEnd if the key is repeated', () => {
|
|
spyOn(testChipInput, 'add');
|
|
|
|
chipInputDirective.separatorKeyCodes = [COMMA];
|
|
fixture.detectChanges();
|
|
|
|
const event = createKeyboardEvent('keydown', COMMA);
|
|
Object.defineProperty(event, 'repeat', {get: () => true});
|
|
dispatchEvent(inputNativeElement, event);
|
|
fixture.detectChanges();
|
|
|
|
expect(testChipInput.add).not.toHaveBeenCalled();
|
|
});
|
|
});
|
|
});
|
|
|
|
@Component({
|
|
template: `
|
|
<mat-form-field>
|
|
<mat-chip-grid #chipGrid [required]="required">
|
|
<mat-chip-row>Hello</mat-chip-row>
|
|
<input [matChipInputFor]="chipGrid"
|
|
[matChipInputAddOnBlur]="addOnBlur"
|
|
(matChipInputTokenEnd)="add($event)"
|
|
[placeholder]="placeholder" />
|
|
</mat-chip-grid>
|
|
</mat-form-field>
|
|
`,
|
|
standalone: false,
|
|
})
|
|
class TestChipInput {
|
|
@ViewChild(MatChipGrid) chipGridInstance: MatChipGrid;
|
|
addOnBlur: boolean = false;
|
|
placeholder = '';
|
|
required = false;
|
|
|
|
add(_: MatChipInputEvent) {}
|
|
}
|