Skip to content

Commit bb4a3c7

Browse files
fix(components/layout)!: replace @angular/animations in text expand repeater component with CSS transitions (#4317)
BREAKING CHANGE `SkyTextExpandRepeaterComponent` has replaced `@angular/animations` with CSS transitions. Tests that interact with text expand repeater may need to add `provideNoopSkyAnimations()` from `@skyux/core` to their `TestBed` providers to disable SKY UX CSS transitions during tests. ``` import { provideNoopSkyAnimations } from '@skyux/core'; TestBed.configureTestingModule({ providers: [provideNoopSkyAnimations()], }); ```
1 parent bbde359 commit bb4a3c7

File tree

10 files changed

+76
-81
lines changed

10 files changed

+76
-81
lines changed

libs/components/code-examples/src/lib/modules/layout/text-expand-repeater/example.component.spec.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
22
import { TestBed } from '@angular/core/testing';
3-
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
3+
import { provideNoopSkyAnimations } from '@skyux/core';
44
import { SkyTextExpandRepeaterHarness } from '@skyux/layout/testing';
55

66
import { LayoutTextExpandRepeaterExampleComponent } from './example.component';
@@ -14,7 +14,8 @@ describe('Text expand inline example', () => {
1414
textExpandRepeaterHarness: SkyTextExpandRepeaterHarness;
1515
}> {
1616
await TestBed.configureTestingModule({
17-
imports: [LayoutTextExpandRepeaterExampleComponent, NoopAnimationsModule],
17+
imports: [LayoutTextExpandRepeaterExampleComponent],
18+
providers: [provideNoopSkyAnimations()],
1819
}).compileComponents();
1920

2021
const fixture = TestBed.createComponent(
Lines changed: 17 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,18 @@
1-
<div [@.disabled]="true">
2-
<sky-text-expand-repeater
3-
[data]="data"
4-
[maxItems]="numItems"
5-
[listStyle]="listStyle"
6-
/>
1+
<sky-text-expand-repeater
2+
[data]="data"
3+
[maxItems]="numItems"
4+
[listStyle]="listStyle"
5+
/>
76

8-
<sky-text-expand-repeater
9-
[data]="customTemplateData"
10-
[itemTemplate]="myTemplate"
11-
[maxItems]="numItems"
12-
[listStyle]="listStyle"
13-
>
14-
<ng-template #myTemplate let-item>
15-
<div class="sky-test-custom-template">
16-
{{ item.text }} {{ item.number }}
17-
</div>
18-
</ng-template>
19-
</sky-text-expand-repeater>
20-
</div>
7+
<sky-text-expand-repeater
8+
[data]="customTemplateData"
9+
[itemTemplate]="myTemplate"
10+
[maxItems]="numItems"
11+
[listStyle]="listStyle"
12+
>
13+
<ng-template #myTemplate let-item>
14+
<div class="sky-test-custom-template">
15+
{{ item.text }} {{ item.number }}
16+
</div>
17+
</ng-template>
18+
</sky-text-expand-repeater>

libs/components/layout/src/lib/modules/text-expand-repeater/fixtures/text-expand-repeater.module.fixture.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
import { NgModule } from '@angular/core';
2-
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
32

43
import { SkyTextExpandRepeaterModule } from '../text-expand-repeater.module';
54

65
import { TextExpandRepeaterTestComponent } from './text-expand-repeater.component.fixture';
76

87
@NgModule({
9-
imports: [NoopAnimationsModule, SkyTextExpandRepeaterModule],
8+
imports: [SkyTextExpandRepeaterModule],
109
exports: [TextExpandRepeaterTestComponent],
1110
declarations: [TextExpandRepeaterTestComponent],
1211
})

libs/components/layout/src/lib/modules/text-expand-repeater/text-expand-repeater-adapter.service.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,4 +37,12 @@ export class SkyTextExpandRepeaterAdapterService {
3737
public removeContainerMaxHeight(containerEl: ElementRef): void {
3838
this.#renderer.removeStyle(containerEl.nativeElement, 'max-height');
3939
}
40+
41+
public setContainerMaxHeight(containerEl: ElementRef, height: number): void {
42+
this.#renderer.setStyle(
43+
containerEl.nativeElement,
44+
'max-height',
45+
`${height}px`,
46+
);
47+
}
4048
}

libs/components/layout/src/lib/modules/text-expand-repeater/text-expand-repeater.component.html

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,28 +3,26 @@
33
<ul
44
#container
55
class="sky-text-expand-repeater-container"
6-
[@expansionAnimation]="{
7-
value: isExpanded(),
8-
params: { transitionHeight: transitionHeight }
9-
}"
6+
skyTransitionEndHandler
7+
transitionPropertyToTrack="max-height"
8+
[transitionTrigger]="isExpanded()"
109
[id]="contentSectionId"
1110
[ngClass]="{
1211
'sky-text-expand-repeater-list-style-none': listStyle() === 'unstyled'
1312
}"
14-
(@expansionAnimation.done)="animationEnd()"
13+
(transitionEnd)="animationEnd()"
1514
>
1615
<ng-template [ngTemplateOutlet]="listItemTemplate" />
1716
</ul>
1817
} @else {
1918
<ol
2019
#container
2120
class="sky-text-expand-repeater-container"
22-
[@expansionAnimation]="{
23-
value: isExpanded(),
24-
params: { transitionHeight: transitionHeight }
25-
}"
21+
skyTransitionEndHandler
22+
transitionPropertyToTrack="max-height"
23+
[transitionTrigger]="isExpanded()"
2624
[id]="contentSectionId"
27-
(@expansionAnimation.done)="animationEnd()"
25+
(transitionEnd)="animationEnd()"
2826
>
2927
<ng-template [ngTemplateOutlet]="listItemTemplate" />
3028
</ol>

libs/components/layout/src/lib/modules/text-expand-repeater/text-expand-repeater.component.scss

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77
height: auto;
88
margin-bottom: 0;
99
margin-top: 0;
10+
transition-duration: var(--sky-transition-time-medium);
11+
transition-property: max-height;
12+
transition-timing-function: ease;
1013
}
1114
.sky-text-expand-repeater-see-more {
1215
white-space: nowrap;

libs/components/layout/src/lib/modules/text-expand-repeater/text-expand-repeater.component.spec.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { ComponentFixture, TestBed } from '@angular/core/testing';
22
import { expect, expectAsync } from '@skyux-sdk/testing';
3+
import { provideNoopSkyAnimations } from '@skyux/core';
34

45
import { TextExpandRepeaterTestComponent } from './fixtures/text-expand-repeater.component.fixture';
56
import { TextExpandRepeaterFixturesModule } from './fixtures/text-expand-repeater.module.fixture';
@@ -20,6 +21,7 @@ describe('Text expand repeater component', () => {
2021
beforeEach(() => {
2122
TestBed.configureTestingModule({
2223
imports: [TextExpandRepeaterFixturesModule],
24+
providers: [provideNoopSkyAnimations()],
2325
});
2426

2527
fixture = TestBed.createComponent(TextExpandRepeaterTestComponent);

libs/components/layout/src/lib/modules/text-expand-repeater/text-expand-repeater.component.ts

Lines changed: 25 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,3 @@
1-
import {
2-
animate,
3-
state,
4-
style,
5-
transition,
6-
trigger,
7-
} from '@angular/animations';
81
import {
92
ChangeDetectorRef,
103
Component,
@@ -30,28 +23,6 @@ import { SkyTextExpandRepeaterListStyleType } from './types/text-expand-repeater
3023
let nextId = 0;
3124

3225
@Component({
33-
animations: [
34-
trigger('expansionAnimation', [
35-
transition(':enter', []),
36-
state(
37-
'true',
38-
style({
39-
maxHeight: '{{transitionHeight}}px',
40-
}),
41-
{ params: { transitionHeight: 0 } },
42-
),
43-
state(
44-
'false',
45-
style({
46-
maxHeight: '{{transitionHeight}}px',
47-
}),
48-
{ params: { transitionHeight: 0 } },
49-
),
50-
transition('true => false', animate('250ms ease')),
51-
transition('false => true', animate('250ms ease')),
52-
transition('void => *', []),
53-
]),
54-
],
5526
selector: 'sky-text-expand-repeater',
5627
templateUrl: './text-expand-repeater.component.html',
5728
styleUrls: ['./text-expand-repeater.component.scss'],
@@ -88,7 +59,6 @@ export class SkyTextExpandRepeaterComponent<TData = unknown> {
8859
public contentSectionId = `sky-text-expand-repeater-content-${++nextId}`;
8960

9061
protected readonly isExpanded = signal(false);
91-
protected transitionHeight = 1;
9262

9363
readonly #resources = inject(SkyLibResourcesService);
9464
readonly #seeMoreText = toSignal(
@@ -131,19 +101,16 @@ export class SkyTextExpandRepeaterComponent<TData = unknown> {
131101
}
132102

133103
public animationEnd(): void {
134-
// Ensure all items that should be hidden are hidden. This is because we need them shown during the animation window for visual purposes.
104+
// Ensure all items that should be hidden are hidden.
135105
if (!this.isExpanded()) {
136106
this.#hideItems();
137107
}
138108

139-
// This set timeout is needed as the `animationEnd` function is called by the angular animation callback prior to the animation setting the style on the element
140-
setTimeout(() => {
141-
const containerEl = this.containerEl();
142-
if (containerEl) {
143-
// Set height back to auto so the browser can change the height as needed with window changes
144-
this.#textExpandRepeaterAdapter.removeContainerMaxHeight(containerEl);
145-
}
146-
});
109+
const containerEl = this.containerEl();
110+
if (containerEl) {
111+
// Set height back to auto so the browser can change the height as needed with window changes
112+
this.#textExpandRepeaterAdapter.removeContainerMaxHeight(containerEl);
113+
}
147114
}
148115

149116
public repeaterExpand(): void {
@@ -158,22 +125,39 @@ export class SkyTextExpandRepeaterComponent<TData = unknown> {
158125
const adapter = this.#textExpandRepeaterAdapter;
159126
const container = this.containerEl();
160127
if (container) {
128+
const containerNative = container.nativeElement;
129+
130+
// Lock at current height as the transition starting point.
131+
const currentHeight = adapter.getContainerHeight(container);
132+
adapter.setContainerMaxHeight(container, currentHeight);
133+
161134
if (expanding) {
162135
this.#showItems();
163136
} else {
164137
this.#hideItems();
165138
}
166-
this.transitionHeight = adapter.getContainerHeight(container);
139+
140+
// Measure the target height.
141+
const targetHeight = expanding
142+
? containerNative.scrollHeight
143+
: adapter.getContainerHeight(container);
144+
167145
if (!expanding) {
168146
// Show all items during animation for visual purposes.
169147
this.#showItems();
170148
}
149+
150+
// Force reflow so the browser registers the starting max-height.
151+
containerNative.getBoundingClientRect();
152+
153+
// Set the target max-height to trigger the CSS transition.
154+
adapter.setContainerMaxHeight(container, targetHeight);
155+
171156
this.isExpanded.set(expanding);
172157
}
173158
}
174159

175160
#setup(): void {
176-
const container = this.containerEl();
177161
const maxItems = this.maxItems();
178162
const value = this.trackedData();
179163
this.expandable = false;
@@ -183,10 +167,6 @@ export class SkyTextExpandRepeaterComponent<TData = unknown> {
183167
if (maxItems && length > maxItems) {
184168
this.expandable = true;
185169
this.#hideItems();
186-
if (container) {
187-
this.transitionHeight =
188-
this.#textExpandRepeaterAdapter.getContainerHeight(container);
189-
}
190170
}
191171
}
192172
this.#changeDetector.detectChanges();
Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,18 @@
11
import { CommonModule } from '@angular/common';
22
import { NgModule } from '@angular/core';
3+
import { _SkyTransitionEndHandlerDirective } from '@skyux/core';
34

45
import { SkyLayoutResourcesModule } from '../shared/sky-layout-resources.module';
56

67
import { SkyTextExpandRepeaterComponent } from './text-expand-repeater.component';
78

89
@NgModule({
910
declarations: [SkyTextExpandRepeaterComponent],
10-
imports: [SkyLayoutResourcesModule, CommonModule],
11+
imports: [
12+
SkyLayoutResourcesModule,
13+
CommonModule,
14+
_SkyTransitionEndHandlerDirective,
15+
],
1116
exports: [SkyTextExpandRepeaterComponent],
1217
})
1318
export class SkyTextExpandRepeaterModule {}

libs/components/layout/testing/src/modules/text-expand-repeater/text-expand-repeater-harness.spec.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
22
import { ComponentFixture, TestBed } from '@angular/core/testing';
3-
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
3+
import { provideNoopSkyAnimations } from '@skyux/core';
44

55
import { TextExpandRepeaterHarnessTestComponent } from './fixtures/text-expand-repeater-harness-test.component';
66
import { SkyTextExpandRepeaterHarness } from './text-expand-repeater-harness';
@@ -15,7 +15,8 @@ describe('Text expand repeater test harness', () => {
1515
fixture: ComponentFixture<TextExpandRepeaterHarnessTestComponent>;
1616
}> {
1717
await TestBed.configureTestingModule({
18-
imports: [TextExpandRepeaterHarnessTestComponent, NoopAnimationsModule],
18+
imports: [TextExpandRepeaterHarnessTestComponent],
19+
providers: [provideNoopSkyAnimations()],
1920
}).compileComponents();
2021

2122
const fixture = TestBed.createComponent(

0 commit comments

Comments
 (0)