Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 9caed2a

Browse filesBrowse files
authored
feat(cdk-experimental/ui-patterns): tabs ui pattern (#30568)
1 parent 3b329e9 commit 9caed2a
Copy full SHA for 9caed2a

File tree

Expand file treeCollapse file tree

26 files changed

+1048
-0
lines changed
Filter options
Expand file treeCollapse file tree

26 files changed

+1048
-0
lines changed
+34Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
load("//tools:defaults.bzl", "ng_web_test_suite")
2+
load("//tools:defaults2.bzl", "ng_project", "ts_project")
3+
4+
package(default_visibility = ["//visibility:public"])
5+
6+
ng_project(
7+
name = "deferred-content",
8+
srcs = [
9+
"deferred-content.ts",
10+
"index.ts",
11+
"public-api.ts",
12+
],
13+
deps = [
14+
"//:node_modules/@angular/core",
15+
],
16+
)
17+
18+
ts_project(
19+
name = "unit_test_sources",
20+
testonly = True,
21+
srcs = [
22+
"deferred-content.spec.ts",
23+
],
24+
deps = [
25+
":deferred-content",
26+
"//:node_modules/@angular/core",
27+
"//:node_modules/@angular/platform-browser",
28+
],
29+
)
30+
31+
ng_web_test_suite(
32+
name = "unit_tests",
33+
deps = [":unit_test_sources"],
34+
)
+87Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import {Component, DebugElement, Directive, effect, inject, signal} from '@angular/core';
2+
import {ComponentFixture, TestBed, waitForAsync} from '@angular/core/testing';
3+
import {DeferredContent, DeferredContentAware} from './deferred-content';
4+
import {By} from '@angular/platform-browser';
5+
6+
describe('DeferredContent', () => {
7+
let fixture: ComponentFixture<TestComponent>;
8+
let collapsible: DebugElement;
9+
10+
beforeEach(waitForAsync(() => {
11+
TestBed.configureTestingModule({
12+
imports: [TestComponent],
13+
});
14+
}));
15+
16+
beforeEach(() => {
17+
fixture = TestBed.createComponent(TestComponent);
18+
collapsible = fixture.debugElement.query(By.directive(Collapsible));
19+
});
20+
21+
it('removes the content when hidden.', async () => {
22+
collapsible.injector.get(Collapsible).contentVisible.set(false);
23+
await fixture.whenStable();
24+
expect(collapsible.nativeElement.innerText).toBe('');
25+
});
26+
27+
it('creates the content when visible.', async () => {
28+
collapsible.injector.get(Collapsible).contentVisible.set(true);
29+
await fixture.whenStable();
30+
expect(collapsible.nativeElement.innerText).toBe('Lazy Content');
31+
});
32+
33+
describe('with preserveContent', () => {
34+
let component: TestComponent;
35+
36+
beforeEach(() => {
37+
component = fixture.componentInstance;
38+
component.preserveContent.set(true);
39+
});
40+
41+
it('creates the content when hidden.', async () => {
42+
collapsible.injector.get(Collapsible).contentVisible.set(false);
43+
await fixture.whenStable();
44+
expect(collapsible.nativeElement.innerText).toBe('Lazy Content');
45+
});
46+
47+
it('creates the content when visible.', async () => {
48+
collapsible.injector.get(Collapsible).contentVisible.set(true);
49+
await fixture.whenStable();
50+
expect(collapsible.nativeElement.innerText).toBe('Lazy Content');
51+
});
52+
});
53+
});
54+
55+
@Directive({
56+
selector: '[collapsible]',
57+
hostDirectives: [{directive: DeferredContentAware, inputs: ['preserveContent']}],
58+
})
59+
class Collapsible {
60+
private readonly _deferredContentAware = inject(DeferredContentAware);
61+
62+
contentVisible = signal(true);
63+
64+
constructor() {
65+
effect(() => this._deferredContentAware.contentVisible.set(this.contentVisible()));
66+
}
67+
}
68+
69+
@Directive({
70+
selector: 'ng-template[collapsibleContent]',
71+
hostDirectives: [DeferredContent],
72+
})
73+
class CollapsibleContent {}
74+
75+
@Component({
76+
template: `
77+
<div collapsible [preserveContent]="preserveContent()">
78+
<ng-template collapsibleContent>
79+
Lazy Content
80+
</ng-template>
81+
</div>
82+
`,
83+
imports: [Collapsible, CollapsibleContent],
84+
})
85+
class TestComponent {
86+
preserveContent = signal(false);
87+
}
+67Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.dev/license
7+
*/
8+
9+
import {
10+
computed,
11+
Directive,
12+
effect,
13+
inject,
14+
input,
15+
TemplateRef,
16+
signal,
17+
ViewContainerRef,
18+
} from '@angular/core';
19+
20+
/**
21+
* A container directive controls the visibility of its content.
22+
*/
23+
@Directive()
24+
export class DeferredContentAware {
25+
contentVisible = signal(false);
26+
readonly preserveContent = input(false);
27+
}
28+
29+
/**
30+
* DeferredContent loads/unloads the content based on the visibility.
31+
* The visibilty signal is sent from a parent directive implements
32+
* DeferredContentAware.
33+
*
34+
* Use this directive as a host directive. For example:
35+
*
36+
* ```ts
37+
* @Directive({
38+
* selector: 'ng-template[cdkAccordionContent]',
39+
* hostDirectives: [DeferredContent],
40+
* })
41+
* class CdkAccordionContent {}
42+
* ```
43+
*/
44+
@Directive()
45+
export class DeferredContent {
46+
private readonly _deferredContentAware = inject(DeferredContentAware);
47+
private readonly _templateRef = inject(TemplateRef);
48+
private readonly _viewContainerRef = inject(ViewContainerRef);
49+
private _isRendered = false;
50+
51+
constructor() {
52+
effect(() => {
53+
if (
54+
this._deferredContentAware.preserveContent() ||
55+
this._deferredContentAware.contentVisible()
56+
) {
57+
if (this._isRendered) return;
58+
this._viewContainerRef.clear();
59+
this._viewContainerRef.createEmbeddedView(this._templateRef);
60+
this._isRendered = true;
61+
} else {
62+
this._viewContainerRef.clear();
63+
this._isRendered = false;
64+
}
65+
});
66+
}
67+
}
+9Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.dev/license
7+
*/
8+
9+
export * from './public-api';
+9Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.dev/license
7+
*/
8+
9+
export {DeferredContentAware, DeferredContent} from './deferred-content';

‎src/cdk-experimental/tabs/BUILD.bazel

Copy file name to clipboard
+18Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
load("//tools:defaults2.bzl", "ng_project")
2+
3+
package(default_visibility = ["//visibility:public"])
4+
5+
ng_project(
6+
name = "tabs",
7+
srcs = [
8+
"index.ts",
9+
"public-api.ts",
10+
"tabs.ts",
11+
],
12+
deps = [
13+
"//src/cdk-experimental/deferred-content",
14+
"//src/cdk-experimental/ui-patterns",
15+
"//src/cdk/a11y",
16+
"//src/cdk/bidi",
17+
],
18+
)

‎src/cdk-experimental/tabs/index.ts

Copy file name to clipboard
+9Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.dev/license
7+
*/
8+
9+
export * from './public-api';
+9Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.dev/license
7+
*/
8+
9+
export {CdkTabs, CdkTabList, CdkTab, CdkTabPanel, CdkTabContent} from './tabs';

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.