Skip to content

Commit 6cfade9

Browse files
committed
feat(sort): default arrow position in MatSortDefaultOptions
closes #23600
1 parent 2f655c9 commit 6cfade9

File tree

5 files changed

+182
-14
lines changed

5 files changed

+182
-14
lines changed

src/material/sort/sort-header.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
-->
1111
<div class="mat-sort-header-container mat-focus-indicator"
1212
[class.mat-sort-header-sorted]="_isSorted()"
13-
[class.mat-sort-header-position-before]="arrowPosition == 'before'"
13+
[class.mat-sort-header-position-before]="arrowPosition === 'before'"
1414
[attr.tabindex]="_isDisabled() ? null : 0"
1515
role="button">
1616

src/material/sort/sort-header.ts

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import {CanDisable, mixinDisabled} from '@angular/material/core';
2424
import {FocusMonitor} from '@angular/cdk/a11y';
2525
import {ENTER, SPACE} from '@angular/cdk/keycodes';
2626
import {merge, Subscription} from 'rxjs';
27-
import {MatSort, MatSortable} from './sort';
27+
import {MAT_SORT_DEFAULT_OPTIONS, MatSort, MatSortable, MatSortDefaultOptions} from './sort';
2828
import {matSortAnimations} from './sort-animations';
2929
import {SortDirection} from './sort-direction';
3030
import {getSortHeaderNotContainedWithinSortError} from './sort-errors';
@@ -127,7 +127,16 @@ export class MatSortHeader extends _MatSortHeaderBase
127127
@Input('mat-sort-header') id: string;
128128

129129
/** Sets the position of the arrow that displays when sorted. */
130-
@Input() arrowPosition: 'before' | 'after' = 'after';
130+
@Input()
131+
get arrowPosition(): 'before' | 'after' { return this._arrowPosition; }
132+
set arrowPosition(v) {
133+
if (v === 'before' || v === 'after') {
134+
this._arrowPosition = v === 'before' ? 'before' : 'after';
135+
} else if (this._defaultOptions?.arrowPosition) {
136+
this._arrowPosition = this._defaultOptions?.arrowPosition;
137+
}
138+
}
139+
private _arrowPosition: 'before' | 'after' = 'after';
131140

132141
/** Overrides the sort start value of the containing MatSort for this MatSortable. */
133142
@Input() start: 'asc' | 'desc';
@@ -148,10 +157,12 @@ export class MatSortHeader extends _MatSortHeaderBase
148157
// `MatSort` is not optionally injected, but just asserted manually w/ better error.
149158
// tslint:disable-next-line: lightweight-tokens
150159
@Optional() public _sort: MatSort,
151-
@Inject('MAT_SORT_HEADER_COLUMN_DEF') @Optional()
160+
@Optional() @Inject('MAT_SORT_HEADER_COLUMN_DEF')
152161
public _columnDef: MatSortHeaderColumnDef,
153162
private _focusMonitor: FocusMonitor,
154-
private _elementRef: ElementRef<HTMLElement>) {
163+
private _elementRef: ElementRef<HTMLElement>,
164+
@Optional() @Inject(MAT_SORT_DEFAULT_OPTIONS)
165+
private _defaultOptions?: MatSortDefaultOptions) {
155166
// Note that we use a string token for the `_columnDef`, because the value is provided both by
156167
// `material/table` and `cdk/table` and we can't have the CDK depending on Material,
157168
// and we want to avoid having the sort header depending on the CDK table because
@@ -162,6 +173,10 @@ export class MatSortHeader extends _MatSortHeaderBase
162173
throw getSortHeaderNotContainedWithinSortError();
163174
}
164175

176+
if (_defaultOptions?.arrowPosition) {
177+
this._arrowPosition = _defaultOptions?.arrowPosition;
178+
}
179+
165180
this._handleStateChanges();
166181
}
167182

src/material/sort/sort.spec.ts

Lines changed: 155 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,9 @@ import {
3030

3131

3232
describe('MatSort', () => {
33-
describe('without default options', () => {
34-
let fixture: ComponentFixture<SimpleMatSortApp>;
35-
let component: SimpleMatSortApp;
33+
describe('without default options', () => {
34+
let fixture: ComponentFixture<SimpleMatSortApp>;
35+
let component: SimpleMatSortApp;
3636

3737
beforeEach(waitForAsync(() => {
3838
TestBed.configureTestingModule({
@@ -44,7 +44,8 @@ describe('MatSort', () => {
4444
MatSortHeaderMissingMatSortApp,
4545
MatSortDuplicateMatSortableIdsApp,
4646
MatSortableMissingIdApp,
47-
MatSortableInvalidDirection
47+
MatSortableInvalidDirection,
48+
MatSortWithArrowPosition
4849
],
4950
}).compileComponents();
5051
}));
@@ -77,7 +78,6 @@ describe('MatSort', () => {
7778
const cdkTableMatSortAppFixture = TestBed.createComponent(CdkTableMatSortApp);
7879
const cdkTableMatSortAppComponent = cdkTableMatSortAppFixture.componentInstance;
7980

80-
cdkTableMatSortAppFixture.detectChanges();
8181
cdkTableMatSortAppFixture.detectChanges();
8282

8383
const sortables = cdkTableMatSortAppComponent.matSort.sortables;
@@ -91,7 +91,6 @@ describe('MatSort', () => {
9191
const matTableMatSortAppFixture = TestBed.createComponent(MatTableMatSortApp);
9292
const matTableMatSortAppComponent = matTableMatSortAppFixture.componentInstance;
9393

94-
matTableMatSortAppFixture.detectChanges();
9594
matTableMatSortAppFixture.detectChanges();
9695

9796
const sortables = matTableMatSortAppComponent.matSort.sortables;
@@ -411,6 +410,58 @@ describe('MatSort', () => {
411410

412411
expect(container.classList.contains('mat-focus-indicator')).toBe(true);
413412
});
413+
414+
it('should render arrows after sort header by default', () => {
415+
const matSortWithArrowPositionFixture = TestBed.createComponent(MatSortWithArrowPosition);
416+
417+
matSortWithArrowPositionFixture.detectChanges();
418+
419+
const containerA = matSortWithArrowPositionFixture.nativeElement
420+
.querySelector('#defaultA .mat-sort-header-container');
421+
const containerB = matSortWithArrowPositionFixture.nativeElement
422+
.querySelector('#defaultB .mat-sort-header-container');
423+
424+
expect(containerA.classList.contains('mat-sort-header-position-before')).toBe(false);
425+
expect(containerB.classList.contains('mat-sort-header-position-before')).toBe(false);
426+
});
427+
428+
it('should render arrows before if appropriate parameter passed', () => {
429+
const matSortWithArrowPositionFixture = TestBed.createComponent(MatSortWithArrowPosition);
430+
const matSortWithArrowPositionComponent = matSortWithArrowPositionFixture.componentInstance;
431+
matSortWithArrowPositionComponent.arrowPosition = 'before';
432+
433+
matSortWithArrowPositionFixture.detectChanges();
434+
435+
const containerA = matSortWithArrowPositionFixture.nativeElement
436+
.querySelector('#defaultA .mat-sort-header-container');
437+
const containerB = matSortWithArrowPositionFixture.nativeElement
438+
.querySelector('#defaultB .mat-sort-header-container');
439+
440+
expect(containerA.classList.contains('mat-sort-header-position-before')).toBe(true);
441+
expect(containerB.classList.contains('mat-sort-header-position-before')).toBe(true);
442+
});
443+
444+
it('should render arrows in proper position based on arrowPosition parameter', () => {
445+
const matSortWithArrowPositionFixture = TestBed.createComponent(MatSortWithArrowPosition);
446+
const matSortWithArrowPositionComponent = matSortWithArrowPositionFixture.componentInstance;
447+
448+
matSortWithArrowPositionFixture.detectChanges();
449+
450+
const containerA = matSortWithArrowPositionFixture.nativeElement
451+
.querySelector('#defaultA .mat-sort-header-container');
452+
const containerB = matSortWithArrowPositionFixture.nativeElement
453+
.querySelector('#defaultB .mat-sort-header-container');
454+
455+
expect(containerA.classList.contains('mat-sort-header-position-before')).toBe(false);
456+
expect(containerB.classList.contains('mat-sort-header-position-before')).toBe(false);
457+
458+
matSortWithArrowPositionComponent.arrowPosition = 'before';
459+
460+
matSortWithArrowPositionFixture.detectChanges();
461+
462+
expect(containerA.classList.contains('mat-sort-header-position-before')).toBe(true);
463+
expect(containerB.classList.contains('mat-sort-header-position-before')).toBe(true);
464+
});
414465
});
415466

416467
describe('with default options', () => {
@@ -449,6 +500,67 @@ describe('MatSort', () => {
449500
testSingleColumnSortDirectionSequence(fixture, ['desc', 'asc']);
450501
});
451502
});
503+
504+
describe('with default arrowPosition', () => {
505+
let fixture: ComponentFixture<MatSortWithArrowPosition>;
506+
let component: MatSortWithArrowPosition;
507+
508+
beforeEach(waitForAsync(() => {
509+
TestBed.configureTestingModule({
510+
imports: [MatSortModule, MatTableModule, CdkTableModule, NoopAnimationsModule],
511+
declarations: [
512+
MatSortWithArrowPosition,
513+
MatSortWithoutInputs
514+
],
515+
providers: [
516+
{
517+
provide: MAT_SORT_DEFAULT_OPTIONS,
518+
useValue: {
519+
disableClear: true,
520+
arrowPosition: 'before'
521+
},
522+
}
523+
],
524+
}).compileComponents();
525+
}));
526+
527+
beforeEach(() => {
528+
fixture = TestBed.createComponent(MatSortWithArrowPosition);
529+
component = fixture.componentInstance;
530+
fixture.detectChanges();
531+
});
532+
533+
it('should render arrows in proper position based on arrowPosition parameter', () => {
534+
const containerA = fixture.nativeElement
535+
.querySelector('#defaultA .mat-sort-header-container');
536+
const containerB = fixture.nativeElement
537+
.querySelector('#defaultB .mat-sort-header-container');
538+
539+
expect(containerA.classList.contains('mat-sort-header-position-before')).toBe(true);
540+
expect(containerB.classList.contains('mat-sort-header-position-before')).toBe(true);
541+
542+
component.arrowPosition = 'after';
543+
544+
fixture.detectChanges();
545+
546+
expect(containerA.classList.contains('mat-sort-header-position-before')).toBe(false);
547+
expect(containerB.classList.contains('mat-sort-header-position-before')).toBe(false);
548+
});
549+
550+
it('should render arrows in proper position', () => {
551+
const matSortWithoutInputsFixture = TestBed.createComponent(MatSortWithoutInputs);
552+
553+
matSortWithoutInputsFixture.detectChanges();
554+
555+
const containerA = matSortWithoutInputsFixture.nativeElement
556+
.querySelector('#defaultA .mat-sort-header-container');
557+
const containerB = matSortWithoutInputsFixture.nativeElement
558+
.querySelector('#defaultB .mat-sort-header-container');
559+
560+
expect(containerA.classList.contains('mat-sort-header-position-before')).toBe(true);
561+
expect(containerB.classList.contains('mat-sort-header-position-before')).toBe(true);
562+
});
563+
});
452564
});
453565

454566
/**
@@ -709,3 +821,40 @@ class MatSortWithoutExplicitInputs {
709821
dispatchMouseEvent(sortElement, event);
710822
}
711823
}
824+
825+
@Component({
826+
template: `
827+
<div matSort>
828+
<div id="defaultA" #defaultA mat-sort-header="defaultA" [arrowPosition]="arrowPosition">
829+
A
830+
</div>
831+
<div id="defaultB" #defaultB mat-sort-header="defaultB" [arrowPosition]="arrowPosition">
832+
B
833+
</div>
834+
</div>
835+
`
836+
})
837+
class MatSortWithArrowPosition {
838+
arrowPosition?: 'before' | 'after';
839+
@ViewChild(MatSort) matSort: MatSort;
840+
@ViewChild('defaultA') defaultA: MatSortHeader;
841+
@ViewChild('defaultB') defaultB: MatSortHeader;
842+
}
843+
844+
@Component({
845+
template: `
846+
<div matSort>
847+
<div id="defaultA" #defaultA mat-sort-header="defaultA">
848+
A
849+
</div>
850+
<div id="defaultB" #defaultB mat-sort-header="defaultB">
851+
B
852+
</div>
853+
</div>
854+
`
855+
})
856+
class MatSortWithoutInputs {
857+
@ViewChild(MatSort) matSort: MatSort;
858+
@ViewChild('defaultA') defaultA: MatSortHeader;
859+
@ViewChild('defaultB') defaultB: MatSortHeader;
860+
}

src/material/sort/sort.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@ export interface Sort {
5858
export interface MatSortDefaultOptions {
5959
/** Whether to disable clearing the sorting state. */
6060
disableClear?: boolean;
61+
/** Sets the position of the arrow that displays when sorted. */
62+
arrowPosition?: 'before' | 'after';
6163
}
6264

6365
/** Injection token to be used to override the default options for `mat-sort`. */

tools/public_api_guard/material/sort.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -100,15 +100,17 @@ export const matSortAnimations: {
100100

101101
// @public
102102
export interface MatSortDefaultOptions {
103+
arrowPosition?: 'before' | 'after';
103104
disableClear?: boolean;
104105
}
105106

106107
// @public
107108
export class MatSortHeader extends _MatSortHeaderBase implements CanDisable, MatSortable, OnDestroy, OnInit, AfterViewInit {
108109
constructor(
109-
_intl: MatSortHeaderIntl, _changeDetectorRef: ChangeDetectorRef, _sort: MatSort, _columnDef: MatSortHeaderColumnDef, _focusMonitor: FocusMonitor, _elementRef: ElementRef<HTMLElement>);
110+
_intl: MatSortHeaderIntl, _changeDetectorRef: ChangeDetectorRef, _sort: MatSort, _columnDef: MatSortHeaderColumnDef, _focusMonitor: FocusMonitor, _elementRef: ElementRef<HTMLElement>, _defaultOptions?: MatSortDefaultOptions | undefined);
110111
_arrowDirection: SortDirection;
111-
arrowPosition: 'before' | 'after';
112+
get arrowPosition(): 'before' | 'after';
113+
set arrowPosition(v: 'before' | 'after');
112114
// (undocumented)
113115
_columnDef: MatSortHeaderColumnDef;
114116
get disableClear(): boolean;
@@ -150,7 +152,7 @@ export class MatSortHeader extends _MatSortHeaderBase implements CanDisable, Mat
150152
// (undocumented)
151153
static ɵcmp: i0.ɵɵComponentDeclaration<MatSortHeader, "[mat-sort-header]", ["matSortHeader"], { "disabled": "disabled"; "id": "mat-sort-header"; "arrowPosition": "arrowPosition"; "start": "start"; "disableClear": "disableClear"; }, {}, never, ["*"]>;
152154
// (undocumented)
153-
static ɵfac: i0.ɵɵFactoryDeclaration<MatSortHeader, [null, null, { optional: true; }, { optional: true; }, null, null]>;
155+
static ɵfac: i0.ɵɵFactoryDeclaration<MatSortHeader, [null, null, { optional: true; }, { optional: true; }, null, null, { optional: true; }]>;
154156
}
155157

156158
// @public @deprecated

0 commit comments

Comments
 (0)