Skip to content

Commit 4f9ab21

Browse files
authoredMay 20, 2025··
feat(cdk-experimental/accordion): add accordion directives, example, and tests (#31134)
1 parent 2971f13 commit 4f9ab21

File tree

23 files changed

+975
-11
lines changed

23 files changed

+975
-11
lines changed
 

‎.ng-dev/commit-message.mts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ export const commitMessage: CommitMessageConfig = {
99
minBodyLengthTypeExcludes: ['docs'],
1010
scopes: [
1111
'multiple', // For when a commit applies to multiple components.
12+
'cdk-experimental/accordion',
1213
'cdk-experimental/column-resize',
1314
'cdk-experimental/combobox',
1415
'cdk-experimental/listbox',
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
load("//tools:defaults.bzl", "ng_project", "ng_web_test_suite", "ts_project")
2+
3+
package(default_visibility = ["//visibility:public"])
4+
5+
ng_project(
6+
name = "accordion",
7+
srcs = [
8+
"accordion.ts",
9+
"index.ts",
10+
"public-api.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+
)
19+
20+
ts_project(
21+
name = "unit_test_sources",
22+
testonly = True,
23+
srcs = [
24+
"accordion.spec.ts",
25+
],
26+
deps = [
27+
":accordion",
28+
"//:node_modules/@angular/core",
29+
"//:node_modules/@angular/platform-browser",
30+
"//src/cdk/testing/private",
31+
],
32+
)
33+
34+
ng_web_test_suite(
35+
name = "unit_tests",
36+
deps = [":unit_test_sources"],
37+
)

‎src/cdk-experimental/accordion/accordion.spec.ts

Lines changed: 437 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
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+
Directive,
11+
input,
12+
ElementRef,
13+
inject,
14+
contentChildren,
15+
afterRenderEffect,
16+
signal,
17+
model,
18+
booleanAttribute,
19+
computed,
20+
WritableSignal,
21+
} from '@angular/core';
22+
import {_IdGenerator} from '@angular/cdk/a11y';
23+
import {Directionality} from '@angular/cdk/bidi';
24+
import {DeferredContent, DeferredContentAware} from '@angular/cdk-experimental/deferred-content';
25+
import {
26+
AccordionGroupPattern,
27+
AccordionPanelPattern,
28+
AccordionTriggerPattern,
29+
} from '../ui-patterns/';
30+
31+
/**
32+
* Represents the content panel of an accordion item. It is controlled by an
33+
* associated `CdkAccordionTrigger`.
34+
*/
35+
@Directive({
36+
selector: '[cdkAccordionPanel]',
37+
exportAs: 'cdkAccordionPanel',
38+
hostDirectives: [
39+
{
40+
directive: DeferredContentAware,
41+
inputs: ['preserveContent'],
42+
},
43+
],
44+
host: {
45+
'class': 'cdk-accordion-panel',
46+
'role': 'region',
47+
'[attr.id]': 'pattern.id()',
48+
'[attr.aria-labelledby]': 'pattern.accordionTrigger()?.id()',
49+
'[attr.inert]': 'pattern.hidden() ? true : null',
50+
},
51+
})
52+
export class CdkAccordionPanel {
53+
/** The DeferredContentAware host directive. */
54+
private readonly _deferredContentAware = inject(DeferredContentAware);
55+
56+
/** A global unique identifier for the panel. */
57+
private readonly _id = inject(_IdGenerator).getId('cdk-accordion-trigger-');
58+
59+
/** A local unique identifier for the panel, used to match with its trigger's value. */
60+
value = input.required<string>();
61+
62+
/** The parent accordion trigger pattern that controls this panel. This is set by CdkAccordionGroup. */
63+
readonly accordionTrigger: WritableSignal<AccordionTriggerPattern | undefined> =
64+
signal(undefined);
65+
66+
/** The UI pattern instance for this panel. */
67+
readonly pattern: AccordionPanelPattern = new AccordionPanelPattern({
68+
id: () => this._id,
69+
value: this.value,
70+
accordionTrigger: () => this.accordionTrigger(),
71+
});
72+
73+
constructor() {
74+
// Connect the panel's hidden state to the DeferredContentAware's visibility.
75+
afterRenderEffect(() => {
76+
this._deferredContentAware.contentVisible.set(!this.pattern.hidden());
77+
});
78+
}
79+
}
80+
81+
/**
82+
* Represents the trigger button for an accordion item. It controls the expansion
83+
* state of an associated `CdkAccordionPanel`.
84+
*/
85+
@Directive({
86+
selector: '[cdkAccordionTrigger]',
87+
exportAs: 'cdkAccordionTrigger',
88+
host: {
89+
'class': 'cdk-accordion-trigger',
90+
'[class.cdk-active]': 'pattern.active()',
91+
'role': 'button',
92+
'[id]': 'pattern.id()',
93+
'[attr.aria-expanded]': 'pattern.expanded()',
94+
'[attr.aria-controls]': 'pattern.controls()',
95+
'[attr.aria-disabled]': 'pattern.disabled()',
96+
'[attr.tabindex]': 'pattern.tabindex()',
97+
'(keydown)': 'pattern.onKeydown($event)',
98+
'(pointerdown)': 'pattern.onPointerdown($event)',
99+
'(focusin)': 'pattern.onFocus($event)',
100+
},
101+
})
102+
export class CdkAccordionTrigger {
103+
/** A global unique identifier for the trigger. */
104+
private readonly _id = inject(_IdGenerator).getId('cdk-accordion-trigger-');
105+
106+
/** A reference to the trigger element. */
107+
private readonly _elementRef = inject(ElementRef);
108+
109+
/** The parent CdkAccordionGroup. */
110+
private readonly _accordionGroup = inject(CdkAccordionGroup);
111+
112+
/** A local unique identifier for the trigger, used to match with its panel's value. */
113+
value = input.required<string>();
114+
115+
/** Whether the trigger is disabled. */
116+
disabled = input(false, {transform: booleanAttribute});
117+
118+
/** The accordion panel pattern controlled by this trigger. This is set by CdkAccordionGroup. */
119+
readonly accordionPanel: WritableSignal<AccordionPanelPattern | undefined> = signal(undefined);
120+
121+
/** The UI pattern instance for this trigger. */
122+
readonly pattern: AccordionTriggerPattern = new AccordionTriggerPattern({
123+
id: () => this._id,
124+
value: this.value,
125+
disabled: this.disabled,
126+
element: () => this._elementRef.nativeElement,
127+
accordionGroup: computed(() => this._accordionGroup.pattern),
128+
accordionPanel: this.accordionPanel,
129+
});
130+
}
131+
132+
/**
133+
* Container for a group of accordion items. It manages the overall state and
134+
* interactions of the accordion, such as keyboard navigation and expansion mode.
135+
*/
136+
@Directive({
137+
selector: '[cdkAccordionGroup]',
138+
exportAs: 'cdkAccordionGroup',
139+
host: {
140+
'class': 'cdk-accordion-group',
141+
},
142+
})
143+
export class CdkAccordionGroup {
144+
/** The CdkAccordionTriggers nested inside this group. */
145+
protected readonly _triggers = contentChildren(CdkAccordionTrigger, {descendants: true});
146+
147+
/** The CdkAccordionPanels nested inside this group. */
148+
protected readonly _panels = contentChildren(CdkAccordionPanel, {descendants: true});
149+
150+
/** The text direction (ltr or rtl). */
151+
readonly textDirection = inject(Directionality).valueSignal;
152+
153+
/** Whether the entire accordion group is disabled. */
154+
disabled = input(false, {transform: booleanAttribute});
155+
156+
/** Whether multiple accordion items can be expanded simultaneously. */
157+
multiExpandable = input(true, {transform: booleanAttribute});
158+
159+
/** The values of the current selected/expanded accordions. */
160+
value = model<string[]>([]);
161+
162+
/** Whether disabled items should be skipped during keyboard navigation. */
163+
skipDisabled = input(true, {transform: booleanAttribute});
164+
165+
/** Whether keyboard navigation should wrap around from the last item to the first, and vice-versa. */
166+
wrap = input(false, {transform: booleanAttribute});
167+
168+
/** The UI pattern instance for this accordion group. */
169+
readonly pattern: AccordionGroupPattern = new AccordionGroupPattern({
170+
...this,
171+
// TODO(ok7sai): Consider making `activeIndex` an internal state in the pattern and call
172+
// `setDefaultState` in the CDK.
173+
activeIndex: signal(0),
174+
items: computed(() => this._triggers().map(trigger => trigger.pattern)),
175+
expandedIds: this.value,
176+
// TODO(ok7sai): Investigate whether an accordion should support horizontal mode.
177+
orientation: () => 'vertical',
178+
});
179+
180+
constructor() {
181+
// Effect to link triggers with their corresponding panels and update the group's items.
182+
afterRenderEffect(() => {
183+
const triggers = this._triggers();
184+
const panels = this._panels();
185+
186+
for (const trigger of triggers) {
187+
const panel = panels.find(p => p.value() === trigger.value());
188+
trigger.accordionPanel.set(panel?.pattern);
189+
if (panel) {
190+
panel.accordionTrigger.set(trigger.pattern);
191+
}
192+
}
193+
});
194+
}
195+
}
196+
197+
/**
198+
* A structural directive that marks the `ng-template` to be used as the content
199+
* for a `CdkAccordionPanel`. This content can be lazily loaded.
200+
*/
201+
@Directive({
202+
selector: 'ng-template[cdkAccordionContent]',
203+
hostDirectives: [DeferredContent],
204+
})
205+
export class CdkAccordionContent {}
Lines 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';
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
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 {
10+
CdkAccordionGroup,
11+
CdkAccordionTrigger,
12+
CdkAccordionPanel,
13+
CdkAccordionContent,
14+
} from './accordion';

‎src/cdk-experimental/config.bzl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# List of all entry-points of the Angular cdk-experimental package.
22
CDK_EXPERIMENTAL_ENTRYPOINTS = [
3+
"accordion",
34
"column-resize",
45
"combobox",
56
"deferred-content",

‎src/cdk-experimental/ui-patterns/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ ts_project(
1010
),
1111
deps = [
1212
"//:node_modules/@angular/core",
13+
"//src/cdk-experimental/ui-patterns/accordion",
1314
"//src/cdk-experimental/ui-patterns/behaviors/signal-like",
1415
"//src/cdk-experimental/ui-patterns/listbox",
1516
"//src/cdk-experimental/ui-patterns/radio",

‎src/cdk-experimental/ui-patterns/accordion/accordion.ts

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,9 +107,15 @@ export class AccordionTriggerPattern {
107107
/** Id of the accordion panel controlled by the trigger. */
108108
controls = computed(() => this.inputs.accordionPanel()?.id());
109109

110+
/** The tabindex of the trigger. */
111+
tabindex = computed(() => (this.inputs.accordionGroup().focusManager.isFocusable(this) ? 0 : -1));
112+
113+
/** Whether the trigger is disabled. Disabling an accordion group disables all the triggers. */
114+
disabled = computed(() => this.inputs.disabled() || this.inputs.accordionGroup().disabled());
115+
110116
constructor(readonly inputs: AccordionTriggerInputs) {
117+
this.id = inputs.id;
111118
this.element = inputs.element;
112-
this.disabled = inputs.disabled;
113119
this.value = inputs.value;
114120
this.accordionGroup = inputs.accordionGroup;
115121
this.accordionPanel = inputs.accordionPanel;
@@ -173,7 +179,16 @@ export class AccordionTriggerPattern {
173179
this.pointerdown().handle(event);
174180
}
175181

176-
private _getItem(e: PointerEvent) {
182+
/** Handles focus events on the trigger. This ensures the tabbing changes the active index. */
183+
onFocus(event: FocusEvent): void {
184+
const item = this._getItem(event);
185+
186+
if (item && this.inputs.accordionGroup().focusManager.isFocusable(item)) {
187+
this.accordionGroup().focusManager.focus(item);
188+
}
189+
}
190+
191+
private _getItem(e: Event) {
177192
if (!(e.target instanceof HTMLElement)) {
178193
return;
179194
}

‎src/cdk-experimental/ui-patterns/behaviors/expansion/expansion.spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -242,10 +242,10 @@ describe('Expansion', () => {
242242
expect(expansion.isExpandable(items[0])).toBeTrue();
243243
});
244244

245-
it('should return true if an item is disabled and skipDisabled is false', () => {
245+
it('should return false if an item is disabled and skipDisabled is false', () => {
246246
const {expansion, items} = getExpansion({skipDisabled: signal(false)});
247247
items[0].disabled.set(true);
248-
expect(expansion.isExpandable(items[0])).toBeTrue();
248+
expect(expansion.isExpandable(items[0])).toBeFalse();
249249
});
250250

251251
it('should return false if an item is disabled and skipDisabled is true', () => {

‎src/cdk-experimental/ui-patterns/behaviors/expansion/expansion.ts

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -76,11 +76,12 @@ export class ListExpansion<T extends ExpansionItem> {
7676

7777
/** Opens the specified item, or the currently active item if none is specified. */
7878
open(item: T = this.activeItem()) {
79-
if (this.isExpandable(item)) {
80-
this.inputs.multiExpandable()
81-
? this.expandedIds.update(ids => ids.concat(item.expansionId()))
82-
: this.expandedIds.set([item.expansionId()]);
79+
if (!this.isExpandable(item)) return;
80+
if (this.isExpanded(item)) return;
81+
if (!this.inputs.multiExpandable()) {
82+
this.closeAll();
8383
}
84+
this.expandedIds.update(ids => ids.concat(item.expansionId()));
8485
}
8586

8687
/** Closes the specified item, or the currently active item if none is specified. */
@@ -116,9 +117,7 @@ export class ListExpansion<T extends ExpansionItem> {
116117

117118
/** Checks whether the specified item is expandable / collapsible. */
118119
isExpandable(item: T) {
119-
return (
120-
!this.inputs.disabled() && this.inputs.focusManager.isFocusable(item) && item.expandable()
121-
);
120+
return !this.inputs.disabled() && !item.disabled() && item.expandable();
122121
}
123122

124123
/** Checks whether the specified item is currently expanded. */

‎src/cdk-experimental/ui-patterns/public-api.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,4 @@ export * from './radio/radio-group';
1212
export * from './radio/radio';
1313
export * from './behaviors/signal-like/signal-like';
1414
export * from './tabs/tabs';
15+
export * from './accordion/accordion';
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
load("//tools:defaults.bzl", "ng_project")
2+
3+
package(default_visibility = ["//visibility:public"])
4+
5+
ng_project(
6+
name = "accordion",
7+
srcs = glob(["**/*.ts"]),
8+
assets = glob([
9+
"**/*.html",
10+
"**/*.css",
11+
]),
12+
deps = [
13+
"//src/cdk-experimental/accordion",
14+
"//src/material/checkbox",
15+
"//src/material/form-field",
16+
"//src/material/icon",
17+
"//src/material/input",
18+
"//src/material/select",
19+
],
20+
)
21+
22+
filegroup(
23+
name = "source-files",
24+
srcs = glob([
25+
"**/*.html",
26+
"**/*.css",
27+
"**/*.ts",
28+
]),
29+
)
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
.example-accordion-controls {
2+
display: flex;
3+
flex-wrap: wrap;
4+
align-items: center;
5+
gap: 16px;
6+
padding-bottom: 16px;
7+
margin-bottom: 16px;
8+
}
9+
10+
.example-accordion-header {
11+
margin: 0;
12+
}
13+
14+
.example-accordion-header .cdk-accordion-trigger {
15+
display: flex;
16+
align-items: center;
17+
justify-content: space-between;
18+
width: 100%;
19+
padding: 12px 16px;
20+
border: 1px solid transparent;
21+
background-color: var(--mat-sys-surface);
22+
cursor: pointer;
23+
border-radius: 4px;
24+
margin-bottom: 4px;
25+
}
26+
27+
.example-accordion-header .cdk-accordion-trigger:hover {
28+
background-color: var(--mat-sys-surface-container-highest);
29+
}
30+
31+
.example-accordion-header .cdk-accordion-trigger.cdk-active {
32+
background-color: var(--mat-sys-surface-dim);
33+
}
34+
35+
.example-accordion-header .cdk-accordion-trigger:focus {
36+
outline: 2px solid var(--mat-sys-primary);
37+
outline-offset: -2px;
38+
}
39+
40+
.example-accordion-header .cdk-accordion-trigger[aria-disabled='true'] {
41+
color: inherit;
42+
opacity: 0.6;
43+
}
44+
45+
.example-accordion-panel {
46+
padding: 16px;
47+
background-color: var(--mat-sys-surface-container);
48+
margin-bottom: 4px;
49+
}
50+
51+
.example-accordion-panel[inert] {
52+
display: none;
53+
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
<div class="example-accordion-controls">
2+
<mat-checkbox [formControl]="wrap">Wrap (ArrowKey-only)</mat-checkbox>
3+
<mat-checkbox [formControl]="multi">Multi</mat-checkbox>
4+
<mat-checkbox [formControl]="disabled">Disabled</mat-checkbox>
5+
<mat-checkbox [formControl]="skipDisabled">Skip Disabled</mat-checkbox>
6+
7+
<mat-form-field subscriptSizing="dynamic" appearance="outline">
8+
<mat-label>Expanded Items</mat-label>
9+
<mat-select [(value)]="expandedIds" multiple>
10+
@for (item of items; track item) {
11+
<mat-option [value]="item">{{item}}</mat-option>
12+
}
13+
</mat-select>
14+
</mat-form-field>
15+
</div>
16+
17+
<div
18+
cdkAccordionGroup
19+
class="example-accordion-group"
20+
[multiExpandable]="multi.value"
21+
[disabled]="disabled.value"
22+
[skipDisabled]="skipDisabled.value"
23+
[wrap]="wrap.value"
24+
[(value)]="expandedIds"
25+
>
26+
<div class="example-accordion-container">
27+
<h3 class="example-accordion-header">
28+
<button cdkAccordionTrigger value="item1">
29+
Item 1 Trigger
30+
<mat-icon aria-hidden="true">{{expansionIcon('item1')()}}</mat-icon>
31+
</button>
32+
</h3>
33+
<div cdkAccordionPanel value="item1" class="example-accordion-panel">
34+
<ng-template cdkAccordionContent>
35+
<p>This is the content for Item 1.</p>
36+
<button>Focusable Button</button>
37+
</ng-template>
38+
</div>
39+
</div>
40+
41+
<div class="example-accordion-container">
42+
<h3 class="example-accordion-header">
43+
<button cdkAccordionTrigger value="item2" disabled>
44+
Item 2 Trigger (disabled)
45+
<mat-icon aria-hidden="true">{{expansionIcon('item2')()}}</mat-icon>
46+
</button>
47+
</h3>
48+
<div cdkAccordionPanel value="item2" class="example-accordion-panel">
49+
<ng-template cdkAccordionContent>
50+
<p>This is the content for Item 2.</p>
51+
<label>Input inside panel: <input type="text"></label>
52+
</ng-template>
53+
</div>
54+
</div>
55+
56+
<div class="example-accordion-container">
57+
<h3 class="example-accordion-header">
58+
<button cdkAccordionTrigger value="item3">
59+
Item 3 Trigger
60+
<mat-icon aria-hidden="true">{{expansionIcon('item3')()}}</mat-icon>
61+
</button>
62+
</h3>
63+
<div
64+
cdkAccordionPanel
65+
value="item3"
66+
class="example-accordion-panel"
67+
>
68+
<ng-template cdkAccordionContent>
69+
<p>This is the content for Item 3.</p>
70+
</ng-template>
71+
</div>
72+
</div>
73+
</div>
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import {Component, computed, model, Signal} from '@angular/core';
2+
import {FormControl, ReactiveFormsModule} from '@angular/forms';
3+
import {MatCheckboxModule} from '@angular/material/checkbox';
4+
import {MatFormFieldModule} from '@angular/material/form-field';
5+
import {MatInputModule} from '@angular/material/input';
6+
import {MatSelectModule} from '@angular/material/select';
7+
import {MatIconModule} from '@angular/material/icon';
8+
import {
9+
CdkAccordionGroup,
10+
CdkAccordionTrigger,
11+
CdkAccordionPanel,
12+
CdkAccordionContent,
13+
} from '@angular/cdk-experimental/accordion';
14+
15+
/** @title Accordion using UI Patterns. */
16+
@Component({
17+
selector: 'cdk-accordion-example',
18+
exportAs: 'cdkAccordionExample',
19+
templateUrl: 'cdk-accordion-example.html',
20+
styleUrl: 'cdk-accordion-example.css',
21+
imports: [
22+
ReactiveFormsModule,
23+
MatIconModule,
24+
MatCheckboxModule,
25+
MatFormFieldModule,
26+
MatInputModule,
27+
MatSelectModule,
28+
CdkAccordionGroup,
29+
CdkAccordionTrigger,
30+
CdkAccordionPanel,
31+
CdkAccordionContent,
32+
],
33+
})
34+
export class CdkAccordionExample {
35+
// Accordion Group Properties
36+
wrap = new FormControl(true, {nonNullable: true});
37+
multi = new FormControl(true, {nonNullable: true});
38+
disabled = new FormControl(false, {nonNullable: true});
39+
skipDisabled = new FormControl(true, {nonNullable: true});
40+
expandedIds = model<string[]>(['item1']);
41+
42+
// Example items
43+
items = ['item1', 'item2', 'item3'];
44+
45+
expansionIcon(item: string): Signal<string> {
46+
return computed(() => (this.expandedIds().includes(item) ? 'expand_less' : 'expand_more'));
47+
}
48+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export {CdkAccordionExample} from './cdk-accordion/cdk-accordion-example';

‎src/dev-app/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ ng_project(
3333
"//src/dev-app/button-toggle",
3434
"//src/dev-app/card",
3535
"//src/dev-app/cdk-dialog",
36+
"//src/dev-app/cdk-experimental-accordion",
3637
"//src/dev-app/cdk-experimental-combobox",
3738
"//src/dev-app/cdk-experimental-listbox",
3839
"//src/dev-app/cdk-experimental-tabs",
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
load("//tools:defaults.bzl", "ng_project")
2+
3+
package(default_visibility = ["//visibility:public"])
4+
5+
ng_project(
6+
name = "cdk-experimental-accordion",
7+
srcs = glob(["**/*.ts"]),
8+
assets = ["cdk-accordion-demo.html"],
9+
deps = ["//src/components-examples/cdk-experimental/accordion"],
10+
)
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<div>
2+
<h4>Accordion using UI Patterns</h4>
3+
<cdk-accordion-example></cdk-accordion-example>
4+
</div>
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
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 {ChangeDetectionStrategy, Component} from '@angular/core';
10+
import {CdkAccordionExample} from '@angular/components-examples/cdk-experimental/accordion';
11+
12+
@Component({
13+
templateUrl: 'cdk-accordion-demo.html',
14+
imports: [CdkAccordionExample],
15+
changeDetection: ChangeDetectionStrategy.OnPush,
16+
})
17+
export class CdkExperimentalAccordionDemo {}

‎src/dev-app/dev-app/dev-app-layout.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ export class DevAppLayout {
6262
{name: 'CDK Experimental Combobox', route: '/cdk-experimental-combobox'},
6363
{name: 'CDK Experimental Listbox', route: '/cdk-experimental-listbox'},
6464
{name: 'CDK Experimental Tabs', route: '/cdk-experimental-tabs'},
65+
{name: 'CDK Experimental Accordion', route: '/cdk-experimental-accordion'},
6566
{name: 'CDK Listbox', route: '/cdk-listbox'},
6667
{name: 'CDK Menu', route: '/cdk-menu'},
6768
{name: 'Autocomplete', route: '/autocomplete'},

‎src/dev-app/routes.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,13 @@ export const DEV_APP_ROUTES: Routes = [
5555
loadComponent: () =>
5656
import('./cdk-experimental-tabs/cdk-tabs-demo').then(m => m.CdkExperimentalTabsDemo),
5757
},
58+
{
59+
path: 'cdk-experimental-accordion',
60+
loadComponent: () =>
61+
import('./cdk-experimental-accordion/cdk-accordion-demo').then(
62+
m => m.CdkExperimentalAccordionDemo,
63+
),
64+
},
5865
{
5966
path: 'cdk-dialog',
6067
loadComponent: () => import('./cdk-dialog/dialog-demo').then(m => m.DialogDemo),

0 commit comments

Comments
 (0)
Please sign in to comment.