Skip to content

Commit ff3d342

Browse files
authoredSep 26, 2024
feat(material/tabs): add alignTabs in MatTabsConfig (#29779)
users can align tabs label via config now rather than adding `mat-tab-align` property on each tab group fixes #29685
1 parent 4e448ec commit ff3d342

File tree

4 files changed

+134
-1
lines changed

4 files changed

+134
-1
lines changed
 

‎src/material/tabs/tab-config.ts

+3
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@ export interface MatTabsConfig {
3939

4040
/** Whether tabs should be stretched to fill the header. */
4141
stretchTabs?: boolean;
42+
43+
/** Alignment for the tabs label. */
44+
alignTabs?: 'start' | 'center' | 'end';
4245
}
4346

4447
/** Injection token that can be used to provide the default options the tabs module. */

‎src/material/tabs/tab-group.spec.ts

+121
Original file line numberDiff line numberDiff line change
@@ -1187,6 +1187,95 @@ describe('MatTabNavBar with a default config', () => {
11871187
});
11881188
});
11891189

1190+
describe('MatTabGroup labels aligned with a config', () => {
1191+
it('should work with start align', () => {
1192+
const fixture = TestBed.configureTestingModule({
1193+
imports: [MatTabsModule, BrowserAnimationsModule, TabsWithAlignConfig],
1194+
providers: [
1195+
{
1196+
provide: MAT_TABS_CONFIG,
1197+
useValue: {alignTabs: 'start'},
1198+
},
1199+
],
1200+
}).createComponent(TabsWithAlignConfig);
1201+
fixture.detectChanges();
1202+
1203+
const tabElement = fixture.nativeElement.querySelector('[mat-align-tabs="start"]');
1204+
expect(tabElement).toBeTruthy();
1205+
});
1206+
1207+
it('should work with center align', () => {
1208+
const fixture = TestBed.configureTestingModule({
1209+
imports: [MatTabsModule, BrowserAnimationsModule, TabsWithAlignConfig],
1210+
providers: [
1211+
{
1212+
provide: MAT_TABS_CONFIG,
1213+
useValue: {alignTabs: 'center'},
1214+
},
1215+
],
1216+
}).createComponent(TabsWithAlignConfig);
1217+
fixture.detectChanges();
1218+
1219+
const tabElement = fixture.nativeElement.querySelector('[mat-align-tabs="center"]');
1220+
expect(tabElement).toBeTruthy();
1221+
});
1222+
1223+
it('should work with end align', () => {
1224+
const fixture = TestBed.configureTestingModule({
1225+
imports: [MatTabsModule, BrowserAnimationsModule, TabsWithAlignConfig],
1226+
providers: [
1227+
{
1228+
provide: MAT_TABS_CONFIG,
1229+
useValue: {alignTabs: 'end'},
1230+
},
1231+
],
1232+
}).createComponent(TabsWithAlignConfig);
1233+
fixture.detectChanges();
1234+
1235+
const tabElement = fixture.nativeElement.querySelector('[mat-align-tabs="end"]');
1236+
expect(tabElement).toBeTruthy();
1237+
});
1238+
1239+
it('should not add align if default config doesnt set align', () => {
1240+
const fixture = TestBed.configureTestingModule({
1241+
imports: [MatTabsModule, BrowserAnimationsModule, TabsWithAlignConfig],
1242+
}).createComponent(TabsWithAlignConfig);
1243+
fixture.detectChanges();
1244+
1245+
let tabElement = fixture.nativeElement.querySelector('[mat-align-tabs="start"]');
1246+
expect(tabElement).toBeFalsy();
1247+
1248+
tabElement = fixture.nativeElement.querySelector('[mat-align-tabs="center"]');
1249+
expect(tabElement).toBeFalsy();
1250+
1251+
tabElement = fixture.nativeElement.querySelector('[mat-align-tabs="end"]');
1252+
expect(tabElement).toBeFalsy();
1253+
1254+
tabElement = fixture.nativeElement.querySelector('.mat-mdc-tab-group');
1255+
expect(tabElement).toBeTruthy();
1256+
});
1257+
1258+
it('should not break if config sets align on already aligned tabs', () => {
1259+
const fixture = TestBed.configureTestingModule({
1260+
imports: [MatTabsModule, BrowserAnimationsModule, TabsWithAlignCenter],
1261+
providers: [{provide: MAT_TABS_CONFIG, useValue: {alignTabs: 'end'}}],
1262+
}).createComponent(TabsWithAlignCenter);
1263+
fixture.detectChanges();
1264+
1265+
let tabElement = fixture.nativeElement.querySelector('[mat-align-tabs="start"]');
1266+
expect(tabElement).toBeFalsy();
1267+
1268+
tabElement = fixture.nativeElement.querySelector('[mat-align-tabs="center"]');
1269+
expect(tabElement).toBeTruthy();
1270+
1271+
tabElement = fixture.nativeElement.querySelector('[mat-align-tabs="end"]');
1272+
expect(tabElement).toBeFalsy();
1273+
1274+
tabElement = fixture.nativeElement.querySelector('.mat-mdc-tab-group');
1275+
expect(tabElement).toBeTruthy();
1276+
});
1277+
});
1278+
11901279
@Component({
11911280
template: `
11921281
<mat-tab-group class="tab-group"
@@ -1547,3 +1636,35 @@ class TabsWithClassesTestApp {
15471636
labelClassList?: string | string[];
15481637
bodyClassList?: string | string[];
15491638
}
1639+
1640+
@Component({
1641+
template: `
1642+
<mat-tab-group>
1643+
<mat-tab>
1644+
First
1645+
</mat-tab>
1646+
<mat-tab>
1647+
Second
1648+
</mat-tab>
1649+
</mat-tab-group>
1650+
`,
1651+
standalone: true,
1652+
imports: [MatTabsModule],
1653+
})
1654+
class TabsWithAlignConfig {}
1655+
1656+
@Component({
1657+
template: `
1658+
<mat-tab-group mat-align-tabs="center">
1659+
<mat-tab>
1660+
First
1661+
</mat-tab>
1662+
<mat-tab>
1663+
Second
1664+
</mat-tab>
1665+
</mat-tab-group>
1666+
`,
1667+
standalone: true,
1668+
imports: [MatTabsModule],
1669+
})
1670+
class TabsWithAlignCenter {}

‎src/material/tabs/tab-group.ts

+7
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ const ENABLE_BACKGROUND_INPUT = true;
7979
'[class.mat-mdc-tab-group-dynamic-height]': 'dynamicHeight',
8080
'[class.mat-mdc-tab-group-inverted-header]': 'headerPosition === "below"',
8181
'[class.mat-mdc-tab-group-stretch-tabs]': 'stretchTabs',
82+
'[attr.mat-align-tabs]': 'alignTabs',
8283
'[style.--mat-tab-animation-duration]': 'animationDuration',
8384
},
8485
standalone: true,
@@ -147,6 +148,10 @@ export class MatTabGroup implements AfterContentInit, AfterContentChecked, OnDes
147148
@Input({alias: 'mat-stretch-tabs', transform: booleanAttribute})
148149
stretchTabs: boolean = true;
149150

151+
/** Alignment for tabs label. */
152+
@Input({alias: 'mat-align-tabs'})
153+
alignTabs: string | null = null;
154+
150155
/** Whether the tab group should grow to the size of the active tab. */
151156
@Input({transform: booleanAttribute})
152157
dynamicHeight: boolean = false;
@@ -293,6 +298,8 @@ export class MatTabGroup implements AfterContentInit, AfterContentChecked, OnDes
293298
: false;
294299
this.stretchTabs =
295300
defaultConfig && defaultConfig.stretchTabs != null ? defaultConfig.stretchTabs : true;
301+
this.alignTabs =
302+
defaultConfig && defaultConfig.alignTabs != null ? defaultConfig.alignTabs : null;
296303
}
297304

298305
/**

‎tools/public_api_guard/material/tabs.md

+3-1
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,7 @@ export class MatTabContent {
246246
// @public
247247
export class MatTabGroup implements AfterContentInit, AfterContentChecked, OnDestroy {
248248
constructor(...args: unknown[]);
249+
alignTabs: string | null;
249250
_allTabs: QueryList<MatTab>;
250251
readonly animationDone: EventEmitter<void>;
251252
get animationDuration(): string;
@@ -315,7 +316,7 @@ export class MatTabGroup implements AfterContentInit, AfterContentChecked, OnDes
315316
_tabs: QueryList<MatTab>;
316317
updatePagination(): void;
317318
// (undocumented)
318-
static ɵcmp: i0.ɵɵComponentDeclaration<MatTabGroup, "mat-tab-group", ["matTabGroup"], { "color": { "alias": "color"; "required": false; }; "fitInkBarToContent": { "alias": "fitInkBarToContent"; "required": false; }; "stretchTabs": { "alias": "mat-stretch-tabs"; "required": false; }; "dynamicHeight": { "alias": "dynamicHeight"; "required": false; }; "selectedIndex": { "alias": "selectedIndex"; "required": false; }; "headerPosition": { "alias": "headerPosition"; "required": false; }; "animationDuration": { "alias": "animationDuration"; "required": false; }; "contentTabIndex": { "alias": "contentTabIndex"; "required": false; }; "disablePagination": { "alias": "disablePagination"; "required": false; }; "disableRipple": { "alias": "disableRipple"; "required": false; }; "preserveContent": { "alias": "preserveContent"; "required": false; }; "backgroundColor": { "alias": "backgroundColor"; "required": false; }; "ariaLabel": { "alias": "aria-label"; "required": false; }; "ariaLabelledby": { "alias": "aria-labelledby"; "required": false; }; }, { "selectedIndexChange": "selectedIndexChange"; "focusChange": "focusChange"; "animationDone": "animationDone"; "selectedTabChange": "selectedTabChange"; }, ["_allTabs"], ["*"], true, never>;
319+
static ɵcmp: i0.ɵɵComponentDeclaration<MatTabGroup, "mat-tab-group", ["matTabGroup"], { "color": { "alias": "color"; "required": false; }; "fitInkBarToContent": { "alias": "fitInkBarToContent"; "required": false; }; "stretchTabs": { "alias": "mat-stretch-tabs"; "required": false; }; "alignTabs": { "alias": "mat-align-tabs"; "required": false; }; "dynamicHeight": { "alias": "dynamicHeight"; "required": false; }; "selectedIndex": { "alias": "selectedIndex"; "required": false; }; "headerPosition": { "alias": "headerPosition"; "required": false; }; "animationDuration": { "alias": "animationDuration"; "required": false; }; "contentTabIndex": { "alias": "contentTabIndex"; "required": false; }; "disablePagination": { "alias": "disablePagination"; "required": false; }; "disableRipple": { "alias": "disableRipple"; "required": false; }; "preserveContent": { "alias": "preserveContent"; "required": false; }; "backgroundColor": { "alias": "backgroundColor"; "required": false; }; "ariaLabel": { "alias": "aria-label"; "required": false; }; "ariaLabelledby": { "alias": "aria-labelledby"; "required": false; }; }, { "selectedIndexChange": "selectedIndexChange"; "focusChange": "focusChange"; "animationDone": "animationDone"; "selectedTabChange": "selectedTabChange"; }, ["_allTabs"], ["*"], true, never>;
319320
// (undocumented)
320321
static ɵfac: i0.ɵɵFactoryDeclaration<MatTabGroup, never>;
321322
}
@@ -507,6 +508,7 @@ export const matTabsAnimations: {
507508

508509
// @public
509510
export interface MatTabsConfig {
511+
alignTabs?: 'start' | 'center' | 'end';
510512
animationDuration?: string;
511513
contentTabIndex?: number;
512514
disablePagination?: boolean;

0 commit comments

Comments
 (0)
Please sign in to comment.