Skip to content

Commit a4e72e1

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

File tree

5 files changed

+160
-14
lines changed

5 files changed

+160
-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: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,13 @@ 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 {
28+
MAT_SORT_DEFAULT_OPTIONS,
29+
MatSort,
30+
MatSortable,
31+
MatSortDefaultOptions,
32+
SortHeaderArrowPosition
33+
} from './sort';
2834
import {matSortAnimations} from './sort-animations';
2935
import {SortDirection} from './sort-direction';
3036
import {getSortHeaderNotContainedWithinSortError} from './sort-errors';
@@ -127,7 +133,7 @@ export class MatSortHeader extends _MatSortHeaderBase
127133
@Input('mat-sort-header') id: string;
128134

129135
/** Sets the position of the arrow that displays when sorted. */
130-
@Input() arrowPosition: 'before' | 'after' = 'after';
136+
@Input() arrowPosition: SortHeaderArrowPosition = 'after';
131137

132138
/** Overrides the sort start value of the containing MatSort for this MatSortable. */
133139
@Input() start: 'asc' | 'desc';
@@ -148,10 +154,12 @@ export class MatSortHeader extends _MatSortHeaderBase
148154
// `MatSort` is not optionally injected, but just asserted manually w/ better error.
149155
// tslint:disable-next-line: lightweight-tokens
150156
@Optional() public _sort: MatSort,
151-
@Inject('MAT_SORT_HEADER_COLUMN_DEF') @Optional()
157+
@Optional() @Inject('MAT_SORT_HEADER_COLUMN_DEF')
152158
public _columnDef: MatSortHeaderColumnDef,
153159
private _focusMonitor: FocusMonitor,
154-
private _elementRef: ElementRef<HTMLElement>) {
160+
private _elementRef: ElementRef<HTMLElement>,
161+
@Optional() @Inject(MAT_SORT_DEFAULT_OPTIONS)
162+
defaultOptions?: MatSortDefaultOptions) {
155163
// Note that we use a string token for the `_columnDef`, because the value is provided both by
156164
// `material/table` and `cdk/table` and we can't have the CDK depending on Material,
157165
// and we want to avoid having the sort header depending on the CDK table because
@@ -162,6 +170,10 @@ export class MatSortHeader extends _MatSortHeaderBase
162170
throw getSortHeaderNotContainedWithinSortError();
163171
}
164172

173+
if (defaultOptions?.arrowPosition) {
174+
this.arrowPosition = defaultOptions?.arrowPosition;
175+
}
176+
165177
this._handleStateChanges();
166178
}
167179

src/material/sort/sort.spec.ts

Lines changed: 131 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,43 @@ describe('MatSort', () => {
449500
testSingleColumnSortDirectionSequence(fixture, ['desc', 'asc']);
450501
});
451502
});
503+
504+
describe('with default arrowPosition', () => {
505+
let fixture: ComponentFixture<MatSortWithoutInputs>;
506+
507+
beforeEach(waitForAsync(() => {
508+
TestBed.configureTestingModule({
509+
imports: [MatSortModule, MatTableModule, CdkTableModule, NoopAnimationsModule],
510+
declarations: [
511+
MatSortWithoutInputs
512+
],
513+
providers: [
514+
{
515+
provide: MAT_SORT_DEFAULT_OPTIONS,
516+
useValue: {
517+
disableClear: true,
518+
arrowPosition: 'before'
519+
},
520+
}
521+
],
522+
}).compileComponents();
523+
}));
524+
525+
beforeEach(() => {
526+
fixture = TestBed.createComponent(MatSortWithoutInputs);
527+
fixture.detectChanges();
528+
});
529+
530+
it('should render arrows in proper position', () => {
531+
const containerA = fixture.nativeElement
532+
.querySelector('#defaultA .mat-sort-header-container');
533+
const containerB = fixture.nativeElement
534+
.querySelector('#defaultB .mat-sort-header-container');
535+
536+
expect(containerA.classList.contains('mat-sort-header-position-before')).toBe(true);
537+
expect(containerB.classList.contains('mat-sort-header-position-before')).toBe(true);
538+
});
539+
});
452540
});
453541

454542
/**
@@ -709,3 +797,40 @@ class MatSortWithoutExplicitInputs {
709797
dispatchMouseEvent(sortElement, event);
710798
}
711799
}
800+
801+
@Component({
802+
template: `
803+
<div matSort>
804+
<div id="defaultA" #defaultA mat-sort-header="defaultA" [arrowPosition]="arrowPosition">
805+
A
806+
</div>
807+
<div id="defaultB" #defaultB mat-sort-header="defaultB" [arrowPosition]="arrowPosition">
808+
B
809+
</div>
810+
</div>
811+
`
812+
})
813+
class MatSortWithArrowPosition {
814+
arrowPosition?: 'before' | 'after';
815+
@ViewChild(MatSort) matSort: MatSort;
816+
@ViewChild('defaultA') defaultA: MatSortHeader;
817+
@ViewChild('defaultB') defaultB: MatSortHeader;
818+
}
819+
820+
@Component({
821+
template: `
822+
<div matSort>
823+
<div id="defaultA" #defaultA mat-sort-header="defaultA">
824+
A
825+
</div>
826+
<div id="defaultB" #defaultB mat-sort-header="defaultB">
827+
B
828+
</div>
829+
</div>
830+
`
831+
})
832+
class MatSortWithoutInputs {
833+
@ViewChild(MatSort) matSort: MatSort;
834+
@ViewChild('defaultA') defaultA: MatSortHeader;
835+
@ViewChild('defaultB') defaultB: MatSortHeader;
836+
}

src/material/sort/sort.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ import {
3333
getSortInvalidDirectionError,
3434
} from './sort-errors';
3535

36+
/** Position of the arrow that displays when sorted. */
37+
export type SortHeaderArrowPosition = 'before' | 'after';
38+
3639
/** Interface for a directive that holds sorting state consumed by `MatSortHeader`. */
3740
export interface MatSortable {
3841
/** The id of the column being sorted. */
@@ -58,6 +61,8 @@ export interface Sort {
5861
export interface MatSortDefaultOptions {
5962
/** Whether to disable clearing the sorting state. */
6063
disableClear?: boolean;
64+
/** Position of the arrow that displays when sorted. */
65+
arrowPosition?: SortHeaderArrowPosition;
6166
}
6267

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

tools/public_api_guard/material/sort.md

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

101101
// @public
102102
export interface MatSortDefaultOptions {
103+
arrowPosition?: SortHeaderArrowPosition;
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);
110111
_arrowDirection: SortDirection;
111-
arrowPosition: 'before' | 'after';
112+
arrowPosition: SortHeaderArrowPosition;
112113
// (undocumented)
113114
_columnDef: MatSortHeaderColumnDef;
114115
get disableClear(): boolean;
@@ -150,7 +151,7 @@ export class MatSortHeader extends _MatSortHeaderBase implements CanDisable, Mat
150151
// (undocumented)
151152
static ɵcmp: i0.ɵɵComponentDeclaration<MatSortHeader, "[mat-sort-header]", ["matSortHeader"], { "disabled": "disabled"; "id": "mat-sort-header"; "arrowPosition": "arrowPosition"; "start": "start"; "disableClear": "disableClear"; }, {}, never, ["*"]>;
152153
// (undocumented)
153-
static ɵfac: i0.ɵɵFactoryDeclaration<MatSortHeader, [null, null, { optional: true; }, { optional: true; }, null, null]>;
154+
static ɵfac: i0.ɵɵFactoryDeclaration<MatSortHeader, [null, null, { optional: true; }, { optional: true; }, null, null, { optional: true; }]>;
154155
}
155156

156157
// @public @deprecated
@@ -181,6 +182,9 @@ export interface Sort {
181182
// @public
182183
export type SortDirection = 'asc' | 'desc' | '';
183184

185+
// @public
186+
export type SortHeaderArrowPosition = 'before' | 'after';
187+
184188
// (No @packageDocumentation comment for this package)
185189

186190
```

0 commit comments

Comments
 (0)