diff --git a/src/material/sort/sort-header.html b/src/material/sort/sort-header.html index cc575585a601..6d6067dfe085 100644 --- a/src/material/sort/sort-header.html +++ b/src/material/sort/sort-header.html @@ -10,7 +10,7 @@ -->
diff --git a/src/material/sort/sort-header.ts b/src/material/sort/sort-header.ts index 3ece3422dde2..72f1e125fe8b 100644 --- a/src/material/sort/sort-header.ts +++ b/src/material/sort/sort-header.ts @@ -24,7 +24,13 @@ import { } from '@angular/core'; import {CanDisable, mixinDisabled} from '@angular/material/core'; import {merge, Subscription} from 'rxjs'; -import {MatSort, MatSortable} from './sort'; +import { + MAT_SORT_DEFAULT_OPTIONS, + MatSort, + MatSortable, + MatSortDefaultOptions, + SortHeaderArrowPosition, +} from './sort'; import {matSortAnimations} from './sort-animations'; import {SortDirection} from './sort-direction'; import {getSortHeaderNotContainedWithinSortError} from './sort-errors'; @@ -134,7 +140,7 @@ export class MatSortHeader @Input('mat-sort-header') id: string; /** Sets the position of the arrow that displays when sorted. */ - @Input() arrowPosition: 'before' | 'after' = 'after'; + @Input() arrowPosition: SortHeaderArrowPosition = 'after'; /** Overrides the sort start value of the containing MatSort for this MatSortable. */ @Input() start: 'asc' | 'desc'; @@ -182,6 +188,9 @@ export class MatSortHeader private _elementRef: ElementRef, /** @breaking-change 14.0.0 _ariaDescriber will be required. */ @Optional() private _ariaDescriber?: AriaDescriber | null, + @Optional() + @Inject(MAT_SORT_DEFAULT_OPTIONS) + defaultOptions?: MatSortDefaultOptions, ) { // Note that we use a string token for the `_columnDef`, because the value is provided both by // `material/table` and `cdk/table` and we can't have the CDK depending on Material, @@ -193,6 +202,10 @@ export class MatSortHeader throw getSortHeaderNotContainedWithinSortError(); } + if (defaultOptions?.arrowPosition) { + this.arrowPosition = defaultOptions?.arrowPosition; + } + this._handleStateChanges(); } diff --git a/src/material/sort/sort.spec.ts b/src/material/sort/sort.spec.ts index ec3688e0b10e..f8ad12dec352 100644 --- a/src/material/sort/sort.spec.ts +++ b/src/material/sort/sort.spec.ts @@ -45,6 +45,8 @@ describe('MatSort', () => { MatSortDuplicateMatSortableIdsApp, MatSortableMissingIdApp, MatSortableInvalidDirection, + MatSortableInvalidDirection, + MatSortWithArrowPosition, ], }).compileComponents(); }), @@ -78,7 +80,6 @@ describe('MatSort', () => { const cdkTableMatSortAppFixture = TestBed.createComponent(CdkTableMatSortApp); const cdkTableMatSortAppComponent = cdkTableMatSortAppFixture.componentInstance; - cdkTableMatSortAppFixture.detectChanges(); cdkTableMatSortAppFixture.detectChanges(); const sortables = cdkTableMatSortAppComponent.matSort.sortables; @@ -92,7 +93,6 @@ describe('MatSort', () => { const matTableMatSortAppFixture = TestBed.createComponent(MatTableMatSortApp); const matTableMatSortAppComponent = matTableMatSortAppFixture.componentInstance; - matTableMatSortAppFixture.detectChanges(); matTableMatSortAppFixture.detectChanges(); const sortables = matTableMatSortAppComponent.matSort.sortables; @@ -439,6 +439,64 @@ describe('MatSort', () => { 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.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', () => { @@ -477,6 +535,45 @@ describe('MatSort', () => { testSingleColumnSortDirectionSequence(fixture, ['desc', 'asc']); }); }); + + describe('with default arrowPosition', () => { + let fixture: ComponentFixture; + + beforeEach( + waitForAsync(() => { + TestBed.configureTestingModule({ + imports: [MatSortModule, MatTableModule, CdkTableModule, NoopAnimationsModule], + declarations: [MatSortWithoutInputs], + providers: [ + { + provide: MAT_SORT_DEFAULT_OPTIONS, + useValue: { + disableClear: true, + arrowPosition: 'before', + }, + }, + ], + }).compileComponents(); + }), + ); + + 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); + }); + }); }); /** @@ -736,3 +833,40 @@ class MatSortWithoutExplicitInputs { dispatchMouseEvent(sortElement, event); } } + +@Component({ + template: ` +
+
+ A +
+
+ B +
+
+ `, +}) +class MatSortWithArrowPosition { + arrowPosition?: 'before' | 'after'; + @ViewChild(MatSort) matSort: MatSort; + @ViewChild('defaultA') defaultA: MatSortHeader; + @ViewChild('defaultB') defaultB: MatSortHeader; +} + +@Component({ + template: ` +
+
+ A +
+
+ B +
+
+ `, +}) +class MatSortWithoutInputs { + @ViewChild(MatSort) matSort: MatSort; + @ViewChild('defaultA') defaultA: MatSortHeader; + @ViewChild('defaultB') defaultB: MatSortHeader; +} diff --git a/src/material/sort/sort.ts b/src/material/sort/sort.ts index 30e1e223c0d0..de01de504c6f 100644 --- a/src/material/sort/sort.ts +++ b/src/material/sort/sort.ts @@ -28,6 +28,9 @@ import { getSortInvalidDirectionError, } from './sort-errors'; +/** Position of the arrow that displays when sorted. */ +export type SortHeaderArrowPosition = 'before' | 'after'; + /** Interface for a directive that holds sorting state consumed by `MatSortHeader`. */ export interface MatSortable { /** The id of the column being sorted. */ @@ -53,6 +56,8 @@ export interface Sort { export interface MatSortDefaultOptions { /** Whether to disable clearing the sorting state. */ disableClear?: boolean; + /** Position of the arrow that displays when sorted. */ + arrowPosition?: SortHeaderArrowPosition; } /** Injection token to be used to override the default options for `mat-sort`. */ diff --git a/tools/public_api_guard/material/sort.md b/tools/public_api_guard/material/sort.md index 5b419bc4e475..cf9520aa453d 100644 --- a/tools/public_api_guard/material/sort.md +++ b/tools/public_api_guard/material/sort.md @@ -97,6 +97,7 @@ export const matSortAnimations: { // @public export interface MatSortDefaultOptions { + arrowPosition?: SortHeaderArrowPosition; disableClear?: boolean; } @@ -104,9 +105,9 @@ export interface MatSortDefaultOptions { export class MatSortHeader extends _MatSortHeaderBase implements CanDisable, MatSortable, OnDestroy, OnInit, AfterViewInit { constructor( _intl: MatSortHeaderIntl, _changeDetectorRef: ChangeDetectorRef, _sort: MatSort, _columnDef: MatSortHeaderColumnDef, _focusMonitor: FocusMonitor, _elementRef: ElementRef, - _ariaDescriber?: AriaDescriber | null | undefined); + _ariaDescriber?: AriaDescriber | null | undefined, defaultOptions?: MatSortDefaultOptions); _arrowDirection: SortDirection; - arrowPosition: 'before' | 'after'; + arrowPosition: SortHeaderArrowPosition; // (undocumented) _columnDef: MatSortHeaderColumnDef; get disableClear(): boolean; @@ -146,7 +147,7 @@ export class MatSortHeader extends _MatSortHeaderBase implements CanDisable, Mat // (undocumented) static ɵcmp: i0.ɵɵComponentDeclaration; // (undocumented) - static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵfac: i0.ɵɵFactoryDeclaration; } // @public @@ -177,6 +178,9 @@ export interface Sort { // @public export type SortDirection = 'asc' | 'desc' | ''; +// @public +export type SortHeaderArrowPosition = 'before' | 'after'; + // (No @packageDocumentation comment for this package) ```