728 lines
25 KiB
TypeScript
728 lines
25 KiB
TypeScript
|
|
import {CollectionViewer, DataSource} from '@angular/cdk/collections';
|
||
|
|
import {CdkTableModule} from '@angular/cdk/table';
|
||
|
|
import {dispatchMouseEvent, wrappedErrorMessage} from '@angular/cdk/testing/private';
|
||
|
|
import {Component, ElementRef, ViewChild, inject} from '@angular/core';
|
||
|
|
import {ComponentFixture, TestBed, fakeAsync, tick, waitForAsync} from '@angular/core/testing';
|
||
|
|
import {MatTableModule} from '@angular/material/table';
|
||
|
|
import {By} from '@angular/platform-browser';
|
||
|
|
import {NoopAnimationsModule} from '@angular/platform-browser/animations';
|
||
|
|
import {Observable} from 'rxjs';
|
||
|
|
import {map} from 'rxjs/operators';
|
||
|
|
import {
|
||
|
|
MAT_SORT_DEFAULT_OPTIONS,
|
||
|
|
MatSort,
|
||
|
|
MatSortHeader,
|
||
|
|
MatSortModule,
|
||
|
|
Sort,
|
||
|
|
SortDirection,
|
||
|
|
} from './index';
|
||
|
|
import {
|
||
|
|
getSortDuplicateSortableIdError,
|
||
|
|
getSortHeaderMissingIdError,
|
||
|
|
getSortHeaderNotContainedWithinSortError,
|
||
|
|
getSortInvalidDirectionError,
|
||
|
|
} from './sort-errors';
|
||
|
|
|
||
|
|
describe('MatSort', () => {
|
||
|
|
describe('without default options', () => {
|
||
|
|
let fixture: ComponentFixture<SimpleMatSortApp>;
|
||
|
|
let component: SimpleMatSortApp;
|
||
|
|
|
||
|
|
beforeEach(waitForAsync(() => {
|
||
|
|
TestBed.configureTestingModule({
|
||
|
|
imports: [
|
||
|
|
MatSortModule,
|
||
|
|
MatTableModule,
|
||
|
|
CdkTableModule,
|
||
|
|
NoopAnimationsModule,
|
||
|
|
SimpleMatSortApp,
|
||
|
|
CdkTableMatSortApp,
|
||
|
|
MatTableMatSortApp,
|
||
|
|
MatSortHeaderMissingMatSortApp,
|
||
|
|
MatSortDuplicateMatSortableIdsApp,
|
||
|
|
MatSortableMissingIdApp,
|
||
|
|
MatSortableInvalidDirection,
|
||
|
|
MatSortableInvalidDirection,
|
||
|
|
MatSortWithArrowPosition,
|
||
|
|
],
|
||
|
|
});
|
||
|
|
}));
|
||
|
|
|
||
|
|
beforeEach(() => {
|
||
|
|
fixture = TestBed.createComponent(SimpleMatSortApp);
|
||
|
|
component = fixture.componentInstance;
|
||
|
|
fixture.detectChanges();
|
||
|
|
});
|
||
|
|
|
||
|
|
it('should have the sort headers register and deregister themselves', () => {
|
||
|
|
const sortables = component.matSort.sortables;
|
||
|
|
expect(sortables.size).toBe(4);
|
||
|
|
expect(sortables.get('defaultA')).toBe(component.defaultA);
|
||
|
|
expect(sortables.get('defaultB')).toBe(component.defaultB);
|
||
|
|
|
||
|
|
fixture.destroy();
|
||
|
|
expect(sortables.size).toBe(0);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('should mark itself as initialized', fakeAsync(() => {
|
||
|
|
let isMarkedInitialized = false;
|
||
|
|
component.matSort.initialized.subscribe(() => (isMarkedInitialized = true));
|
||
|
|
|
||
|
|
tick();
|
||
|
|
expect(isMarkedInitialized).toBeTruthy();
|
||
|
|
}));
|
||
|
|
|
||
|
|
it('should use the column definition if used within a cdk table', () => {
|
||
|
|
const cdkTableMatSortAppFixture = TestBed.createComponent(CdkTableMatSortApp);
|
||
|
|
const cdkTableMatSortAppComponent = cdkTableMatSortAppFixture.componentInstance;
|
||
|
|
|
||
|
|
cdkTableMatSortAppFixture.detectChanges();
|
||
|
|
|
||
|
|
const sortables = cdkTableMatSortAppComponent.matSort.sortables;
|
||
|
|
expect(sortables.size).toBe(3);
|
||
|
|
expect(sortables.has('column_a')).toBe(true);
|
||
|
|
expect(sortables.has('column_b')).toBe(true);
|
||
|
|
expect(sortables.has('column_c')).toBe(true);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('should use the column definition if used within an mat table', () => {
|
||
|
|
const matTableMatSortAppFixture = TestBed.createComponent(MatTableMatSortApp);
|
||
|
|
const matTableMatSortAppComponent = matTableMatSortAppFixture.componentInstance;
|
||
|
|
|
||
|
|
matTableMatSortAppFixture.detectChanges();
|
||
|
|
|
||
|
|
const sortables = matTableMatSortAppComponent.matSort.sortables;
|
||
|
|
expect(sortables.size).toBe(3);
|
||
|
|
expect(sortables.has('column_a')).toBe(true);
|
||
|
|
expect(sortables.has('column_b')).toBe(true);
|
||
|
|
expect(sortables.has('column_c')).toBe(true);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('should be able to cycle from asc -> desc from either start point', () => {
|
||
|
|
component.disableClear = true;
|
||
|
|
|
||
|
|
component.start = 'asc';
|
||
|
|
fixture.changeDetectorRef.markForCheck();
|
||
|
|
testSingleColumnSortDirectionSequence(fixture, ['asc', 'desc']);
|
||
|
|
|
||
|
|
// Reverse directions
|
||
|
|
component.start = 'desc';
|
||
|
|
fixture.changeDetectorRef.markForCheck();
|
||
|
|
testSingleColumnSortDirectionSequence(fixture, ['desc', 'asc']);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('should be able to cycle asc -> desc -> [none]', () => {
|
||
|
|
component.start = 'asc';
|
||
|
|
fixture.changeDetectorRef.markForCheck();
|
||
|
|
testSingleColumnSortDirectionSequence(fixture, ['asc', 'desc', '']);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('should be able to cycle desc -> asc -> [none]', () => {
|
||
|
|
component.start = 'desc';
|
||
|
|
fixture.changeDetectorRef.markForCheck();
|
||
|
|
testSingleColumnSortDirectionSequence(fixture, ['desc', 'asc', '']);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('should allow for the cycling the sort direction to be disabled per column', () => {
|
||
|
|
const container = fixture.nativeElement.querySelector('#defaultA .mat-sort-header-container');
|
||
|
|
|
||
|
|
component.sort('defaultA');
|
||
|
|
expect(component.matSort.direction).toBe('asc');
|
||
|
|
expect(container.getAttribute('tabindex')).toBe('0');
|
||
|
|
expect(container.getAttribute('role')).toBe('button');
|
||
|
|
|
||
|
|
component.disabledColumnSort = true;
|
||
|
|
fixture.changeDetectorRef.markForCheck();
|
||
|
|
fixture.detectChanges();
|
||
|
|
|
||
|
|
component.sort('defaultA');
|
||
|
|
expect(component.matSort.direction).toBe('asc');
|
||
|
|
expect(container.hasAttribute('tabindex')).toBe(false);
|
||
|
|
expect(container.hasAttribute('role')).toBe(false);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('should allow for the cycling the sort direction to be disabled for all columns', () => {
|
||
|
|
const container = fixture.nativeElement.querySelector('#defaultA .mat-sort-header-container');
|
||
|
|
|
||
|
|
component.sort('defaultA');
|
||
|
|
expect(component.matSort.active).toBe('defaultA');
|
||
|
|
expect(component.matSort.direction).toBe('asc');
|
||
|
|
expect(container.getAttribute('tabindex')).toBe('0');
|
||
|
|
|
||
|
|
component.disableAllSort = true;
|
||
|
|
fixture.changeDetectorRef.markForCheck();
|
||
|
|
fixture.detectChanges();
|
||
|
|
|
||
|
|
component.sort('defaultA');
|
||
|
|
expect(component.matSort.active).toBe('defaultA');
|
||
|
|
expect(component.matSort.direction).toBe('asc');
|
||
|
|
expect(container.getAttribute('tabindex')).toBeFalsy();
|
||
|
|
|
||
|
|
component.sort('defaultB');
|
||
|
|
expect(component.matSort.active).toBe('defaultA');
|
||
|
|
expect(component.matSort.direction).toBe('asc');
|
||
|
|
expect(container.getAttribute('tabindex')).toBeFalsy();
|
||
|
|
});
|
||
|
|
|
||
|
|
it('should reset sort direction when a different column is sorted', () => {
|
||
|
|
component.sort('defaultA');
|
||
|
|
expect(component.matSort.active).toBe('defaultA');
|
||
|
|
expect(component.matSort.direction).toBe('asc');
|
||
|
|
|
||
|
|
component.sort('defaultA');
|
||
|
|
expect(component.matSort.active).toBe('defaultA');
|
||
|
|
expect(component.matSort.direction).toBe('desc');
|
||
|
|
|
||
|
|
component.sort('defaultB');
|
||
|
|
expect(component.matSort.active).toBe('defaultB');
|
||
|
|
expect(component.matSort.direction).toBe('asc');
|
||
|
|
});
|
||
|
|
|
||
|
|
it(
|
||
|
|
'should throw an error if an MatSortable is not contained within an MatSort ' + 'directive',
|
||
|
|
() => {
|
||
|
|
expect(() =>
|
||
|
|
TestBed.createComponent(MatSortHeaderMissingMatSortApp).detectChanges(),
|
||
|
|
).toThrowError(wrappedErrorMessage(getSortHeaderNotContainedWithinSortError()));
|
||
|
|
},
|
||
|
|
);
|
||
|
|
|
||
|
|
it('should throw an error if two MatSortables have the same id', () => {
|
||
|
|
expect(() =>
|
||
|
|
TestBed.createComponent(MatSortDuplicateMatSortableIdsApp).detectChanges(),
|
||
|
|
).toThrowError(wrappedErrorMessage(getSortDuplicateSortableIdError('duplicateId')));
|
||
|
|
});
|
||
|
|
|
||
|
|
it('should throw an error if an MatSortable is missing an id', () => {
|
||
|
|
expect(() => TestBed.createComponent(MatSortableMissingIdApp).detectChanges()).toThrowError(
|
||
|
|
wrappedErrorMessage(getSortHeaderMissingIdError()),
|
||
|
|
);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('should throw an error if the provided direction is invalid', () => {
|
||
|
|
expect(() =>
|
||
|
|
TestBed.createComponent(MatSortableInvalidDirection).detectChanges(),
|
||
|
|
).toThrowError(wrappedErrorMessage(getSortInvalidDirectionError('ascending')));
|
||
|
|
});
|
||
|
|
|
||
|
|
it('should allow let MatSortable override the default sort parameters', () => {
|
||
|
|
testSingleColumnSortDirectionSequence(fixture, ['asc', 'desc', '']);
|
||
|
|
|
||
|
|
testSingleColumnSortDirectionSequence(fixture, ['desc', 'asc', ''], 'overrideStart');
|
||
|
|
|
||
|
|
testSingleColumnSortDirectionSequence(fixture, ['asc', 'desc'], 'overrideDisableClear');
|
||
|
|
});
|
||
|
|
|
||
|
|
it('should apply the aria-sort label to the header when sorted', () => {
|
||
|
|
const sortHeaderElement = fixture.nativeElement.querySelector('#defaultA');
|
||
|
|
expect(sortHeaderElement.getAttribute('aria-sort')).toBe('none');
|
||
|
|
|
||
|
|
component.sort('defaultA');
|
||
|
|
fixture.detectChanges();
|
||
|
|
expect(sortHeaderElement.getAttribute('aria-sort')).toBe('ascending');
|
||
|
|
|
||
|
|
component.sort('defaultA');
|
||
|
|
fixture.detectChanges();
|
||
|
|
expect(sortHeaderElement.getAttribute('aria-sort')).toBe('descending');
|
||
|
|
|
||
|
|
component.sort('defaultA');
|
||
|
|
fixture.detectChanges();
|
||
|
|
expect(sortHeaderElement.getAttribute('aria-sort')).toBe('none');
|
||
|
|
});
|
||
|
|
|
||
|
|
it('should not render the arrow if sorting is disabled for that column', fakeAsync(() => {
|
||
|
|
const sortHeaderElement = fixture.nativeElement.querySelector('#defaultA');
|
||
|
|
|
||
|
|
// Switch sorting to a different column before asserting.
|
||
|
|
component.sort('defaultB');
|
||
|
|
fixture.componentInstance.disabledColumnSort = true;
|
||
|
|
fixture.changeDetectorRef.markForCheck();
|
||
|
|
fixture.detectChanges();
|
||
|
|
tick();
|
||
|
|
fixture.detectChanges();
|
||
|
|
|
||
|
|
expect(sortHeaderElement.querySelector('.mat-sort-header-arrow')).toBeFalsy();
|
||
|
|
}));
|
||
|
|
|
||
|
|
it('should render the arrow if a disabled column is being sorted by', fakeAsync(() => {
|
||
|
|
const sortHeaderElement = fixture.nativeElement.querySelector('#defaultA');
|
||
|
|
|
||
|
|
component.sort('defaultA');
|
||
|
|
fixture.componentInstance.disabledColumnSort = true;
|
||
|
|
fixture.changeDetectorRef.markForCheck();
|
||
|
|
fixture.detectChanges();
|
||
|
|
tick();
|
||
|
|
fixture.detectChanges();
|
||
|
|
|
||
|
|
expect(sortHeaderElement.querySelector('.mat-sort-header-arrow')).toBeTruthy();
|
||
|
|
}));
|
||
|
|
|
||
|
|
it('should have a focus indicator', () => {
|
||
|
|
const headerNativeElement = fixture.debugElement.query(
|
||
|
|
By.directive(MatSortHeader),
|
||
|
|
)!.nativeElement;
|
||
|
|
const container = headerNativeElement.querySelector('.mat-sort-header-container');
|
||
|
|
|
||
|
|
expect(container.classList.contains('mat-focus-indicator')).toBe(true);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('should add a default aria description to sort buttons', () => {
|
||
|
|
const sortButton = fixture.nativeElement.querySelector('.mat-sort-header-container');
|
||
|
|
const descriptionId = sortButton.getAttribute('aria-describedby');
|
||
|
|
expect(descriptionId).toBeDefined();
|
||
|
|
|
||
|
|
const descriptionElement = document.getElementById(descriptionId);
|
||
|
|
expect(descriptionElement?.textContent).toBe('Sort');
|
||
|
|
});
|
||
|
|
|
||
|
|
it('should add a custom aria description to sort buttons', () => {
|
||
|
|
const sortButton = fixture.nativeElement.querySelector(
|
||
|
|
'#defaultB .mat-sort-header-container',
|
||
|
|
);
|
||
|
|
let descriptionId = sortButton.getAttribute('aria-describedby');
|
||
|
|
expect(descriptionId).toBeDefined();
|
||
|
|
|
||
|
|
let descriptionElement = document.getElementById(descriptionId);
|
||
|
|
expect(descriptionElement?.textContent).toBe('Sort second column');
|
||
|
|
|
||
|
|
fixture.componentInstance.secondColumnDescription = 'Sort 2nd column';
|
||
|
|
fixture.changeDetectorRef.markForCheck();
|
||
|
|
fixture.detectChanges();
|
||
|
|
descriptionId = sortButton.getAttribute('aria-describedby');
|
||
|
|
descriptionElement = document.getElementById(descriptionId);
|
||
|
|
expect(descriptionElement?.textContent).toBe('Sort 2nd column');
|
||
|
|
});
|
||
|
|
|
||
|
|
it('should render arrows after sort header by default', () => {
|
||
|
|
const matSortWithArrowPositionFixture = TestBed.createComponent(MatSortWithArrowPosition);
|
||
|
|
|
||
|
|
matSortWithArrowPositionFixture.detectChanges();
|
||
|
|
|
||
|
|
const containerA = matSortWithArrowPositionFixture.nativeElement.querySelector(
|
||
|
|
'#defaultA .mat-sort-header-container',
|
||
|
|
);
|
||
|
|
const containerB = matSortWithArrowPositionFixture.nativeElement.querySelector(
|
||
|
|
'#defaultB .mat-sort-header-container',
|
||
|
|
);
|
||
|
|
|
||
|
|
expect(containerA.classList.contains('mat-sort-header-position-before')).toBe(false);
|
||
|
|
expect(containerB.classList.contains('mat-sort-header-position-before')).toBe(false);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('should render arrows before if appropriate parameter passed', () => {
|
||
|
|
const matSortWithArrowPositionFixture = TestBed.createComponent(MatSortWithArrowPosition);
|
||
|
|
const matSortWithArrowPositionComponent = matSortWithArrowPositionFixture.componentInstance;
|
||
|
|
matSortWithArrowPositionComponent.arrowPosition = 'before';
|
||
|
|
|
||
|
|
matSortWithArrowPositionFixture.detectChanges();
|
||
|
|
|
||
|
|
const containerA = matSortWithArrowPositionFixture.nativeElement.querySelector(
|
||
|
|
'#defaultA .mat-sort-header-container',
|
||
|
|
);
|
||
|
|
const containerB = matSortWithArrowPositionFixture.nativeElement.querySelector(
|
||
|
|
'#defaultB .mat-sort-header-container',
|
||
|
|
);
|
||
|
|
|
||
|
|
expect(containerA.classList.contains('mat-sort-header-position-before')).toBe(true);
|
||
|
|
expect(containerB.classList.contains('mat-sort-header-position-before')).toBe(true);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('should render arrows in proper position based on arrowPosition parameter', () => {
|
||
|
|
const matSortWithArrowPositionFixture = TestBed.createComponent(MatSortWithArrowPosition);
|
||
|
|
const matSortWithArrowPositionComponent = matSortWithArrowPositionFixture.componentInstance;
|
||
|
|
|
||
|
|
matSortWithArrowPositionFixture.detectChanges();
|
||
|
|
|
||
|
|
const containerA = matSortWithArrowPositionFixture.nativeElement.querySelector(
|
||
|
|
'#defaultA .mat-sort-header-container',
|
||
|
|
);
|
||
|
|
const containerB = matSortWithArrowPositionFixture.nativeElement.querySelector(
|
||
|
|
'#defaultB .mat-sort-header-container',
|
||
|
|
);
|
||
|
|
|
||
|
|
expect(containerA.classList.contains('mat-sort-header-position-before')).toBe(false);
|
||
|
|
expect(containerB.classList.contains('mat-sort-header-position-before')).toBe(false);
|
||
|
|
|
||
|
|
matSortWithArrowPositionComponent.arrowPosition = 'before';
|
||
|
|
matSortWithArrowPositionFixture.changeDetectorRef.markForCheck();
|
||
|
|
|
||
|
|
matSortWithArrowPositionFixture.detectChanges();
|
||
|
|
|
||
|
|
expect(containerA.classList.contains('mat-sort-header-position-before')).toBe(true);
|
||
|
|
expect(containerB.classList.contains('mat-sort-header-position-before')).toBe(true);
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
describe('with default options', () => {
|
||
|
|
let fixture: ComponentFixture<MatSortWithoutExplicitInputs>;
|
||
|
|
let component: MatSortWithoutExplicitInputs;
|
||
|
|
|
||
|
|
beforeEach(waitForAsync(() => {
|
||
|
|
TestBed.configureTestingModule({
|
||
|
|
imports: [
|
||
|
|
MatSortModule,
|
||
|
|
MatTableModule,
|
||
|
|
CdkTableModule,
|
||
|
|
NoopAnimationsModule,
|
||
|
|
MatSortWithoutExplicitInputs,
|
||
|
|
],
|
||
|
|
providers: [
|
||
|
|
{
|
||
|
|
provide: MAT_SORT_DEFAULT_OPTIONS,
|
||
|
|
useValue: {
|
||
|
|
disableClear: true,
|
||
|
|
},
|
||
|
|
},
|
||
|
|
],
|
||
|
|
});
|
||
|
|
}));
|
||
|
|
|
||
|
|
beforeEach(() => {
|
||
|
|
fixture = TestBed.createComponent(MatSortWithoutExplicitInputs);
|
||
|
|
component = fixture.componentInstance;
|
||
|
|
fixture.detectChanges();
|
||
|
|
});
|
||
|
|
|
||
|
|
it('should be able to cycle from asc -> desc from either start point', () => {
|
||
|
|
component.start = 'asc';
|
||
|
|
testSingleColumnSortDirectionSequence(fixture, ['asc', 'desc']);
|
||
|
|
|
||
|
|
// Reverse directions
|
||
|
|
component.start = 'desc';
|
||
|
|
testSingleColumnSortDirectionSequence(fixture, ['desc', 'asc']);
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
describe('with default arrowPosition', () => {
|
||
|
|
let fixture: ComponentFixture<MatSortWithoutInputs>;
|
||
|
|
|
||
|
|
beforeEach(waitForAsync(() => {
|
||
|
|
TestBed.configureTestingModule({
|
||
|
|
imports: [
|
||
|
|
MatSortModule,
|
||
|
|
MatTableModule,
|
||
|
|
CdkTableModule,
|
||
|
|
NoopAnimationsModule,
|
||
|
|
MatSortWithoutInputs,
|
||
|
|
],
|
||
|
|
providers: [
|
||
|
|
{
|
||
|
|
provide: MAT_SORT_DEFAULT_OPTIONS,
|
||
|
|
useValue: {
|
||
|
|
disableClear: true,
|
||
|
|
arrowPosition: 'before',
|
||
|
|
},
|
||
|
|
},
|
||
|
|
],
|
||
|
|
});
|
||
|
|
}));
|
||
|
|
|
||
|
|
beforeEach(() => {
|
||
|
|
fixture = TestBed.createComponent(MatSortWithoutInputs);
|
||
|
|
fixture.detectChanges();
|
||
|
|
});
|
||
|
|
|
||
|
|
it('should render arrows in proper position', () => {
|
||
|
|
const containerA = fixture.nativeElement.querySelector(
|
||
|
|
'#defaultA .mat-sort-header-container',
|
||
|
|
);
|
||
|
|
const containerB = fixture.nativeElement.querySelector(
|
||
|
|
'#defaultB .mat-sort-header-container',
|
||
|
|
);
|
||
|
|
|
||
|
|
expect(containerA.classList.contains('mat-sort-header-position-before')).toBe(true);
|
||
|
|
expect(containerB.classList.contains('mat-sort-header-position-before')).toBe(true);
|
||
|
|
});
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Performs a sequence of sorting on a single column to see if the sort directions are
|
||
|
|
* consistent with expectations. Detects any changes in the fixture to reflect any changes in
|
||
|
|
* the inputs and resets the MatSort to remove any side effects from previous tests.
|
||
|
|
*/
|
||
|
|
function testSingleColumnSortDirectionSequence(
|
||
|
|
fixture: ComponentFixture<SimpleMatSortApp | MatSortWithoutExplicitInputs>,
|
||
|
|
expectedSequence: SortDirection[],
|
||
|
|
id: SimpleMatSortAppColumnIds = 'defaultA',
|
||
|
|
) {
|
||
|
|
// Detect any changes that were made in preparation for this sort sequence
|
||
|
|
fixture.detectChanges();
|
||
|
|
|
||
|
|
// Reset the sort to make sure there are no side affects from previous tests
|
||
|
|
const component = fixture.componentInstance;
|
||
|
|
component.matSort.active = '';
|
||
|
|
component.matSort.direction = '';
|
||
|
|
|
||
|
|
// Run through the sequence to confirm the order
|
||
|
|
const actualSequence = expectedSequence.map(() => {
|
||
|
|
component.sort(id);
|
||
|
|
|
||
|
|
// Check that the sort event's active sort is consistent with the MatSort
|
||
|
|
expect(component.matSort.active).toBe(id);
|
||
|
|
expect(component.latestSortEvent.active).toBe(id);
|
||
|
|
|
||
|
|
// Check that the sort event's direction is consistent with the MatSort
|
||
|
|
expect(component.matSort.direction).toBe(component.latestSortEvent.direction);
|
||
|
|
return component.matSort.direction;
|
||
|
|
});
|
||
|
|
expect(actualSequence).toEqual(expectedSequence);
|
||
|
|
|
||
|
|
// Expect that performing one more sort will loop it back to the beginning.
|
||
|
|
component.sort(id);
|
||
|
|
expect(component.matSort.direction).toBe(expectedSequence[0]);
|
||
|
|
}
|
||
|
|
|
||
|
|
/** Column IDs of the SimpleMatSortApp for typing of function params in the component (e.g. sort) */
|
||
|
|
type SimpleMatSortAppColumnIds = 'defaultA' | 'defaultB' | 'overrideStart' | 'overrideDisableClear';
|
||
|
|
|
||
|
|
@Component({
|
||
|
|
template: `
|
||
|
|
<div matSort
|
||
|
|
[matSortActive]="active"
|
||
|
|
[matSortDisabled]="disableAllSort"
|
||
|
|
[matSortStart]="start"
|
||
|
|
[matSortDirection]="direction"
|
||
|
|
[matSortDisableClear]="disableClear"
|
||
|
|
(matSortChange)="latestSortEvent = $event">
|
||
|
|
<div id="defaultA"
|
||
|
|
#defaultA
|
||
|
|
mat-sort-header="defaultA"
|
||
|
|
[disabled]="disabledColumnSort">
|
||
|
|
A
|
||
|
|
</div>
|
||
|
|
<div id="defaultB"
|
||
|
|
#defaultB
|
||
|
|
mat-sort-header="defaultB"
|
||
|
|
[sortActionDescription]="secondColumnDescription">
|
||
|
|
B
|
||
|
|
</div>
|
||
|
|
<div id="overrideStart"
|
||
|
|
#overrideStart
|
||
|
|
mat-sort-header="overrideStart" start="desc">
|
||
|
|
D
|
||
|
|
</div>
|
||
|
|
<div id="overrideDisableClear"
|
||
|
|
#overrideDisableClear
|
||
|
|
mat-sort-header="overrideDisableClear"
|
||
|
|
disableClear>
|
||
|
|
E
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
`,
|
||
|
|
standalone: true,
|
||
|
|
imports: [MatSortModule, MatTableModule, CdkTableModule],
|
||
|
|
})
|
||
|
|
class SimpleMatSortApp {
|
||
|
|
elementRef = inject<ElementRef<HTMLElement>>(ElementRef);
|
||
|
|
latestSortEvent: Sort;
|
||
|
|
active: string;
|
||
|
|
start: SortDirection = 'asc';
|
||
|
|
direction: SortDirection = '';
|
||
|
|
disableClear: boolean;
|
||
|
|
disabledColumnSort = false;
|
||
|
|
disableAllSort = false;
|
||
|
|
secondColumnDescription = 'Sort second column';
|
||
|
|
|
||
|
|
@ViewChild(MatSort) matSort: MatSort;
|
||
|
|
@ViewChild('defaultA') defaultA: MatSortHeader;
|
||
|
|
@ViewChild('defaultB') defaultB: MatSortHeader;
|
||
|
|
@ViewChild('overrideStart') overrideStart: MatSortHeader;
|
||
|
|
@ViewChild('overrideDisableClear') overrideDisableClear: MatSortHeader;
|
||
|
|
|
||
|
|
sort(id: SimpleMatSortAppColumnIds) {
|
||
|
|
this.dispatchMouseEvent(id, 'click');
|
||
|
|
}
|
||
|
|
|
||
|
|
dispatchMouseEvent(id: SimpleMatSortAppColumnIds, event: string) {
|
||
|
|
const sortElement = this.elementRef.nativeElement.querySelector(`#${id}`)!;
|
||
|
|
dispatchMouseEvent(sortElement, event);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
class FakeDataSource extends DataSource<any> {
|
||
|
|
connect(collectionViewer: CollectionViewer): Observable<any[]> {
|
||
|
|
return collectionViewer.viewChange.pipe(map(() => []));
|
||
|
|
}
|
||
|
|
disconnect() {}
|
||
|
|
}
|
||
|
|
|
||
|
|
@Component({
|
||
|
|
template: `
|
||
|
|
<cdk-table [dataSource]="dataSource" matSort>
|
||
|
|
<ng-container cdkColumnDef="column_a">
|
||
|
|
<cdk-header-cell *cdkHeaderCellDef #sortHeaderA mat-sort-header> Column A </cdk-header-cell>
|
||
|
|
<cdk-cell *cdkCellDef="let row"> {{row.a}} </cdk-cell>
|
||
|
|
</ng-container>
|
||
|
|
|
||
|
|
<ng-container cdkColumnDef="column_b">
|
||
|
|
<cdk-header-cell *cdkHeaderCellDef #sortHeaderB mat-sort-header> Column B </cdk-header-cell>
|
||
|
|
<cdk-cell *cdkCellDef="let row"> {{row.b}} </cdk-cell>
|
||
|
|
</ng-container>
|
||
|
|
|
||
|
|
<ng-container cdkColumnDef="column_c">
|
||
|
|
<cdk-header-cell *cdkHeaderCellDef #sortHeaderC mat-sort-header> Column C </cdk-header-cell>
|
||
|
|
<cdk-cell *cdkCellDef="let row"> {{row.c}} </cdk-cell>
|
||
|
|
</ng-container>
|
||
|
|
|
||
|
|
<cdk-header-row *cdkHeaderRowDef="columnsToRender"></cdk-header-row>
|
||
|
|
<cdk-row *cdkRowDef="let row; columns: columnsToRender"></cdk-row>
|
||
|
|
</cdk-table>
|
||
|
|
`,
|
||
|
|
standalone: true,
|
||
|
|
imports: [MatSortModule, MatTableModule, CdkTableModule],
|
||
|
|
})
|
||
|
|
class CdkTableMatSortApp {
|
||
|
|
@ViewChild(MatSort) matSort: MatSort;
|
||
|
|
|
||
|
|
dataSource = new FakeDataSource();
|
||
|
|
columnsToRender = ['column_a', 'column_b', 'column_c'];
|
||
|
|
}
|
||
|
|
|
||
|
|
@Component({
|
||
|
|
template: `
|
||
|
|
<mat-table [dataSource]="dataSource" matSort>
|
||
|
|
<ng-container matColumnDef="column_a">
|
||
|
|
<mat-header-cell *matHeaderCellDef #sortHeaderA mat-sort-header> Column A </mat-header-cell>
|
||
|
|
<mat-cell *matCellDef="let row"> {{row.a}} </mat-cell>
|
||
|
|
</ng-container>
|
||
|
|
|
||
|
|
<ng-container matColumnDef="column_b">
|
||
|
|
<mat-header-cell *matHeaderCellDef #sortHeaderB mat-sort-header> Column B </mat-header-cell>
|
||
|
|
<mat-cell *matCellDef="let row"> {{row.b}} </mat-cell>
|
||
|
|
</ng-container>
|
||
|
|
|
||
|
|
<ng-container matColumnDef="column_c">
|
||
|
|
<mat-header-cell *matHeaderCellDef #sortHeaderC mat-sort-header> Column C </mat-header-cell>
|
||
|
|
<mat-cell *matCellDef="let row"> {{row.c}} </mat-cell>
|
||
|
|
</ng-container>
|
||
|
|
|
||
|
|
<mat-header-row *matHeaderRowDef="columnsToRender"></mat-header-row>
|
||
|
|
<mat-row *matRowDef="let row; columns: columnsToRender"></mat-row>
|
||
|
|
</mat-table>
|
||
|
|
`,
|
||
|
|
standalone: true,
|
||
|
|
imports: [MatSortModule, MatTableModule, CdkTableModule],
|
||
|
|
})
|
||
|
|
class MatTableMatSortApp {
|
||
|
|
@ViewChild(MatSort) matSort: MatSort;
|
||
|
|
|
||
|
|
dataSource = new FakeDataSource();
|
||
|
|
columnsToRender = ['column_a', 'column_b', 'column_c'];
|
||
|
|
}
|
||
|
|
|
||
|
|
@Component({
|
||
|
|
template: `<div mat-sort-header="a"> A </div>`,
|
||
|
|
standalone: true,
|
||
|
|
imports: [MatSortModule, MatTableModule, CdkTableModule],
|
||
|
|
})
|
||
|
|
class MatSortHeaderMissingMatSortApp {}
|
||
|
|
|
||
|
|
@Component({
|
||
|
|
template: `
|
||
|
|
<div matSort>
|
||
|
|
<div mat-sort-header="duplicateId"> A </div>
|
||
|
|
<div mat-sort-header="duplicateId"> A </div>
|
||
|
|
</div>
|
||
|
|
`,
|
||
|
|
standalone: true,
|
||
|
|
imports: [MatSortModule, MatTableModule, CdkTableModule],
|
||
|
|
})
|
||
|
|
class MatSortDuplicateMatSortableIdsApp {}
|
||
|
|
|
||
|
|
@Component({
|
||
|
|
template: `
|
||
|
|
<div matSort>
|
||
|
|
<div mat-sort-header> A </div>
|
||
|
|
</div>
|
||
|
|
`,
|
||
|
|
standalone: true,
|
||
|
|
imports: [MatSortModule, MatTableModule, CdkTableModule],
|
||
|
|
})
|
||
|
|
class MatSortableMissingIdApp {}
|
||
|
|
|
||
|
|
@Component({
|
||
|
|
template: `
|
||
|
|
<div matSort matSortDirection="ascending">
|
||
|
|
<div mat-sort-header="a"> A </div>
|
||
|
|
</div>
|
||
|
|
`,
|
||
|
|
standalone: true,
|
||
|
|
imports: [MatSortModule, MatTableModule, CdkTableModule],
|
||
|
|
})
|
||
|
|
class MatSortableInvalidDirection {}
|
||
|
|
|
||
|
|
@Component({
|
||
|
|
template: `
|
||
|
|
<div matSort
|
||
|
|
[matSortActive]="active"
|
||
|
|
[matSortStart]="start"
|
||
|
|
(matSortChange)="latestSortEvent = $event">
|
||
|
|
<div id="defaultA" #defaultA mat-sort-header="defaultA">
|
||
|
|
A
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
`,
|
||
|
|
standalone: true,
|
||
|
|
imports: [MatSortModule, MatTableModule, CdkTableModule],
|
||
|
|
})
|
||
|
|
class MatSortWithoutExplicitInputs {
|
||
|
|
elementRef = inject<ElementRef<HTMLElement>>(ElementRef);
|
||
|
|
latestSortEvent: Sort;
|
||
|
|
active: string;
|
||
|
|
start: SortDirection = 'asc';
|
||
|
|
|
||
|
|
@ViewChild(MatSort) matSort: MatSort;
|
||
|
|
@ViewChild('defaultA') defaultA: MatSortHeader;
|
||
|
|
|
||
|
|
sort(id: SimpleMatSortAppColumnIds) {
|
||
|
|
this.dispatchMouseEvent(id, 'click');
|
||
|
|
}
|
||
|
|
|
||
|
|
dispatchMouseEvent(id: SimpleMatSortAppColumnIds, event: string) {
|
||
|
|
const sortElement = this.elementRef.nativeElement.querySelector(`#${id}`)!;
|
||
|
|
dispatchMouseEvent(sortElement, event);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
@Component({
|
||
|
|
template: `
|
||
|
|
<div matSort>
|
||
|
|
<div id="defaultA" #defaultA mat-sort-header="defaultA" [arrowPosition]="arrowPosition">
|
||
|
|
A
|
||
|
|
</div>
|
||
|
|
<div id="defaultB" #defaultB mat-sort-header="defaultB" [arrowPosition]="arrowPosition">
|
||
|
|
B
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
`,
|
||
|
|
standalone: true,
|
||
|
|
imports: [MatSortModule, MatTableModule, CdkTableModule],
|
||
|
|
})
|
||
|
|
class MatSortWithArrowPosition {
|
||
|
|
arrowPosition?: 'before' | 'after';
|
||
|
|
@ViewChild(MatSort) matSort: MatSort;
|
||
|
|
@ViewChild('defaultA') defaultA: MatSortHeader;
|
||
|
|
@ViewChild('defaultB') defaultB: MatSortHeader;
|
||
|
|
}
|
||
|
|
|
||
|
|
@Component({
|
||
|
|
template: `
|
||
|
|
<div matSort>
|
||
|
|
<div id="defaultA" #defaultA mat-sort-header="defaultA">
|
||
|
|
A
|
||
|
|
</div>
|
||
|
|
<div id="defaultB" #defaultB mat-sort-header="defaultB">
|
||
|
|
B
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
`,
|
||
|
|
standalone: true,
|
||
|
|
imports: [MatSortModule, MatTableModule, CdkTableModule],
|
||
|
|
})
|
||
|
|
class MatSortWithoutInputs {
|
||
|
|
@ViewChild(MatSort) matSort: MatSort;
|
||
|
|
@ViewChild('defaultA') defaultA: MatSortHeader;
|
||
|
|
@ViewChild('defaultB') defaultB: MatSortHeader;
|
||
|
|
}
|