Skip to content

Commit 0eb9012

Browse files
authored
fix(material/tabs): switch away from animations module (#30281)
Reworks the tabs so they don't depend on the animations module anymore. This is both simpler and avoids some of the pitfalls of the animations module.
1 parent 306e437 commit 0eb9012

File tree

10 files changed

+238
-236
lines changed

10 files changed

+238
-236
lines changed

src/material/tabs/tab-body.html

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
1-
<div class="mat-mdc-tab-body-content" #content
2-
[@translateTab]="{
3-
value: _position,
4-
params: {animationDuration: animationDuration}
5-
}"
6-
(@translateTab.start)="_onTranslateTabStarted($event)"
7-
(@translateTab.done)="_translateTabComplete.next($event)"
8-
cdkScrollable>
1+
<div
2+
class="mat-mdc-tab-body-content"
3+
#content
4+
cdkScrollable
5+
[class.mat-tab-body-content-left]="_position === 'left'"
6+
[class.mat-tab-body-content-right]="_position === 'right'"
7+
[class.mat-tab-body-content-can-animate]="_position === 'center' || _previousPosition === 'center'">
98
<ng-template matTabBodyHost></ng-template>
109
</div>

src/material/tabs/tab-body.scss

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -27,19 +27,33 @@
2727
.mat-mdc-tab-body-content {
2828
height: 100%;
2929
overflow: auto;
30+
transform: none;
31+
visibility: hidden;
32+
33+
.mat-tab-body-animating > &,
34+
.mat-mdc-tab-body-active > & {
35+
visibility: visible;
36+
}
3037

3138
.mat-mdc-tab-group-dynamic-height & {
3239
overflow: hidden;
3340
}
41+
}
42+
43+
.mat-tab-body-content-can-animate {
44+
// Note: there's a 1ms delay so that transition events
45+
// still fire even if the duration is set to zero.
46+
transition: transform var(--mat-tab-animation-duration) 1ms cubic-bezier(0.35, 0, 0.25, 1);
3447

35-
// Usually the `visibility: hidden` added by the animation is enough to prevent focus from
36-
// entering the collapsed content, but children with their own `visibility` can override it.
37-
// This is a fallback that completely hides the content when the element becomes hidden.
38-
// Note that we can't do this in the animation definition, because the style gets recomputed too
39-
// late, breaking the animation because Angular didn't have time to figure out the target height.
40-
// This can also be achieved with JS, but it has issues when starting an animation before
41-
// the previous one has finished.
42-
&[style*='visibility: hidden'] {
43-
display: none;
48+
.mat-mdc-tab-body-wrapper._mat-animation-noopable & {
49+
transition: none;
4450
}
4551
}
52+
53+
.mat-tab-body-content-left {
54+
transform: translate3d(-100%, 0, 0);
55+
}
56+
57+
.mat-tab-body-content-right {
58+
transform: translate3d(100%, 0, 0);
59+
}

src/material/tabs/tab-body.spec.ts

Lines changed: 6 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -36,74 +36,12 @@ describe('MatTabBody', () => {
3636
});
3737
}));
3838

39-
describe('when initialized as center', () => {
40-
let fixture: ComponentFixture<SimpleTabBodyApp>;
41-
42-
it('should be center position if origin is unchanged', () => {
43-
fixture = TestBed.createComponent(SimpleTabBodyApp);
44-
fixture.componentInstance.position = 0;
45-
fixture.detectChanges();
46-
47-
expect(fixture.componentInstance.tabBody._position).toBe('center');
48-
});
49-
50-
it('should be center position if origin is explicitly set to null', () => {
51-
fixture = TestBed.createComponent(SimpleTabBodyApp);
52-
fixture.componentInstance.position = 0;
53-
54-
// It can happen that the `origin` is explicitly set to null through the Angular input
55-
// binding. This test should ensure that the body does properly such origin value.
56-
// The `MatTab` class sets the origin by default to null. See related issue: #12455
57-
fixture.componentInstance.origin = null;
58-
fixture.detectChanges();
59-
60-
expect(fixture.componentInstance.tabBody._position).toBe('center');
61-
});
62-
63-
describe('in LTR direction', () => {
64-
beforeEach(() => {
65-
dir = 'ltr';
66-
fixture = TestBed.createComponent(SimpleTabBodyApp);
67-
});
68-
it('should be left-origin-center position with negative or zero origin', () => {
69-
fixture.componentInstance.position = 0;
70-
fixture.componentInstance.origin = 0;
71-
fixture.detectChanges();
72-
73-
expect(fixture.componentInstance.tabBody._position).toBe('left-origin-center');
74-
});
75-
76-
it('should be right-origin-center position with positive nonzero origin', () => {
77-
fixture.componentInstance.position = 0;
78-
fixture.componentInstance.origin = 1;
79-
fixture.detectChanges();
80-
81-
expect(fixture.componentInstance.tabBody._position).toBe('right-origin-center');
82-
});
83-
});
84-
85-
describe('in RTL direction', () => {
86-
beforeEach(() => {
87-
dir = 'rtl';
88-
fixture = TestBed.createComponent(SimpleTabBodyApp);
89-
});
90-
91-
it('should be right-origin-center position with negative or zero origin', () => {
92-
fixture.componentInstance.position = 0;
93-
fixture.componentInstance.origin = 0;
94-
fixture.detectChanges();
95-
96-
expect(fixture.componentInstance.tabBody._position).toBe('right-origin-center');
97-
});
98-
99-
it('should be left-origin-center position with positive nonzero origin', () => {
100-
fixture.componentInstance.position = 0;
101-
fixture.componentInstance.origin = 1;
102-
fixture.detectChanges();
39+
it('should be center position if origin is unchanged', () => {
40+
const fixture = TestBed.createComponent(SimpleTabBodyApp);
41+
fixture.componentInstance.position = 0;
42+
fixture.detectChanges();
10343

104-
expect(fixture.componentInstance.tabBody._position).toBe('left-origin-center');
105-
});
106-
});
44+
expect(fixture.componentInstance.tabBody._position).toBe('center');
10745
});
10846

10947
describe('should properly set the position in LTR', () => {
@@ -213,14 +151,13 @@ describe('MatTabBody', () => {
213151
@Component({
214152
template: `
215153
<ng-template>Tab Body Content</ng-template>
216-
<mat-tab-body [content]="content()" [position]="position" [origin]="origin"></mat-tab-body>
154+
<mat-tab-body [content]="content()" [position]="position"></mat-tab-body>
217155
`,
218156
imports: [PortalModule, MatRippleModule, MatTabBody],
219157
})
220158
class SimpleTabBodyApp implements AfterViewInit {
221159
content = signal<TemplatePortal | undefined>(undefined);
222160
position: number;
223-
origin: number | null;
224161

225162
@ViewChild(MatTabBody) tabBody: MatTabBody;
226163
@ViewChild(TemplateRef) template: TemplateRef<any>;

0 commit comments

Comments
 (0)