Skip to content

Commit 50cd9b4

Browse files
authored
feat: accordion to accordion-cdk (#263)
1 parent 66b11f5 commit 50cd9b4

10 files changed

+124
-106
lines changed

eslint.config.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ module.exports = tseslint.config(
3333
'no-restricted-imports': [
3434
'error',
3535
{
36-
patterns: ['*ngverse*', '*/ngverse/*'],
36+
patterns: [],
3737
},
3838
],
3939
},

package-lock.json

+15-26
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
"@angular/router": "^19.0.6",
4141
"@angular/ssr": "^19.0.7",
4242
"@docsearch/js": "^3.8.3",
43+
"@ngverse/icons-lu": "^0.0.12",
4344
"@tailwindcss/postcss": "^4.0.8",
4445
"@types/file-saver": "^2.0.7",
4546
"copyfiles": "^2.4.1",

projects/docs/src/app/blueprint/api-info/api-info.component.html

+29-12
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,49 @@
1-
@if (apiInfo().articleLink) {
1+
@let api = apiInfo();
2+
3+
@if (api.articleLink) {
24
<p class="mb-6">
35
Read the
46
<a
5-
class="inline-flex items-center gap-1 underline"
6-
[href]="apiInfo().articleLink"
7+
class="inline-flex items-center gap-1 !underline"
8+
[href]="api.articleLink"
79
target="_blank"
8-
>Article <app-icon width="18" height="18" name="external-link"></app-icon>
10+
>Article
11+
<app-icon width="18" height="18" name="external-link"></app-icon>
912
</a>
1013
</p>
1114
}
1215

1316
<h5 class="my-0 pb-2 text-lg font-medium">API</h5>
14-
<app-divider class="mb-6"></app-divider>
15-
@if (apiInfo(); as api) {
17+
<div class="flex items-center gap-2">
1618
@if (api.ariaLink) {
17-
<p class="mb-6">
18-
The component adheres to
19-
<a [href]="api.ariaLink" target="_blank" class="underline">WAI-ARIA</a>
20-
standards for accessibility.
21-
</p>
19+
<a
20+
[href]="api.ariaLink"
21+
target="_blank"
22+
class="inline-flex items-center gap-1 rounded-xl bg-slate-200 px-2 py-1 text-xs"
23+
>
24+
WAI-ARIA <external-link-icon size="14"> </external-link-icon>
25+
</a>
2226
}
27+
@if (api.reliesOn) {
28+
<a
29+
[href]="api.reliesOn"
30+
target="_blank"
31+
class="inline-flex items-center gap-1 rounded-xl bg-slate-200 px-2 py-1 text-xs"
32+
>
33+
Relies on<external-link-icon size="14"> </external-link-icon>
34+
</a>
35+
}
36+
</div>
37+
38+
<app-divider class="mb-6"></app-divider>
39+
@if (api; as api) {
2340
@if (api.ariaDescription) {
2441
<p class="mb-6">WAI-ARIA: {{ api.ariaDescription }}</p>
2542
}
2643
@if (api.stylesInGlobal) {
2744
<p class="mb-6">
2845
The styles can be found in the
29-
<a [routerLink]="['/doc/theming']" class="underline">ngverse.css</a>
46+
<a [routerLink]="['/doc/theming']" class="!underline">ngverse.css</a>
3047
file.
3148
</p>
3249
}

projects/docs/src/app/blueprint/api-info/api-info.component.ts

+11-3
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
1-
import { Component, input } from '@angular/core';
2-
import { RouterLink } from '@angular/router';
31
import { DividerComponent } from '@/ui/divider/divider.component';
42
import { IconComponent } from '@/ui/icon/icon.component';
3+
import { Component, input } from '@angular/core';
4+
import { RouterLink } from '@angular/router';
5+
import { ExternalLinkIcon } from '@ngverse/icons-lu';
56
import {
67
ApiEntity,
78
ApiInputsComponent,
89
} from './api-inputs/api-inputs.component';
910
export interface ApiInfo {
11+
reliesOn?: string;
1012
articleLink?: string;
1113
entities: ApiEntity[];
1214
description?: string;
@@ -16,7 +18,13 @@ export interface ApiInfo {
1618
}
1719
@Component({
1820
selector: 'doc-api-info',
19-
imports: [ApiInputsComponent, RouterLink, IconComponent, DividerComponent],
21+
imports: [
22+
ApiInputsComponent,
23+
ExternalLinkIcon,
24+
RouterLink,
25+
IconComponent,
26+
DividerComponent,
27+
],
2028
templateUrl: './api-info.component.html',
2129
styleUrl: './api-info.component.css',
2230
})

projects/docs/src/app/features/accordion-page/accordion-page.component.ts

+54-2
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@ import {
33
ApiInfo,
44
ApiInfoComponent,
55
} from '../../blueprint/api-info/api-info.component';
6-
import { EMPTY_API_INPUT_DEFAULT_VALUE } from '../../blueprint/api-info/api-inputs/api-inputs.component';
6+
import {
7+
EMPTY_API_INPUT_DEFAULT_VALUE,
8+
VOID_API_RETURN_TYPE,
9+
} from '../../blueprint/api-info/api-inputs/api-inputs.component';
710
import { BlueprintPageComponent } from '../../blueprint/blueprint-page/blueprint-page.component';
811
import { CommandInstallationComponent } from '../../blueprint/command-installation/command-installation.component';
912
import { ShowCaseComponent } from '../../blueprint/show-case/show-case.component';
@@ -49,6 +52,7 @@ export class AccordionPageComponent {
4952
];
5053

5154
apiInfo: ApiInfo = {
55+
reliesOn: 'https://material.angular.io/cdk/accordion/overview',
5256
ariaLink: 'https://www.w3.org/WAI/ARIA/apg/patterns/accordion/',
5357
entities: [
5458
{
@@ -64,6 +68,18 @@ export class AccordionPageComponent {
6468
default: 'false',
6569
},
6670
],
71+
methods: [
72+
{
73+
name: 'closeAll',
74+
returnType: VOID_API_RETURN_TYPE,
75+
description: 'closes all accordion-items',
76+
},
77+
{
78+
name: 'openAll',
79+
returnType: VOID_API_RETURN_TYPE,
80+
description: 'opens all accordion-items',
81+
},
82+
],
6783
},
6884
{
6985
name: 'AccordionItemComponent',
@@ -87,10 +103,46 @@ export class AccordionPageComponent {
87103
{
88104
name: 'expanded',
89105
type: 'boolean',
90-
description: 'opens the accordion-item',
106+
description: 'expands the accordion-item',
91107
default: 'false',
92108
},
93109
],
110+
outputs: [
111+
{
112+
name: 'opened',
113+
value: VOID_API_RETURN_TYPE,
114+
description: 'emits when the accordion item is opened',
115+
},
116+
{
117+
name: 'closed',
118+
value: VOID_API_RETURN_TYPE,
119+
description: 'emits when the accordion item is closed',
120+
},
121+
{
122+
name: 'destroyed',
123+
value: VOID_API_RETURN_TYPE,
124+
description: 'emits when the accordion item is destroyed',
125+
},
126+
],
127+
methods: [
128+
{
129+
name: 'toggle',
130+
description: 'toggles the accordion-item',
131+
returnType: 'void',
132+
params: [],
133+
},
134+
{
135+
name: 'open',
136+
description: 'opens the accordion-item',
137+
returnType: 'void',
138+
params: [],
139+
},
140+
{
141+
name: 'close',
142+
description: 'closes the accordion-item',
143+
returnType: 'void',
144+
},
145+
],
94146
},
95147
{
96148
name: 'AccordionBodyComponent',

projects/ngverse/src/lib/accordion/accordion-item.component.html

+4-4
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,20 @@ <h3 role="heading">
22
<button
33
class="peer flex w-full cursor-pointer items-center focus-visible:outline-1 focus-visible:outline-offset-4 focus-visible:outline-ring disabled:text-disabled-foreground aria-expanded:font-medium"
44
(click)="toggle()"
5-
[disabled]="disabled()"
6-
[attr.aria-expanded]="isOpen()"
5+
[disabled]="disabled"
6+
[attr.aria-expanded]="expanded"
77
[id]="accordionTriggerId"
88
[attr.aria-controls]="accordionBodyId"
99
>
1010
{{ label() }}
1111
<ng-content select="app-accordion-header"></ng-content>
1212
<app-expand-icon
13-
[class.rotate-180]="isOpen()"
13+
[class.rotate-180]="expanded"
1414
class="ml-auto transition-transform"
1515
></app-expand-icon>
1616
</button>
1717
</h3>
18-
@if (isOpen()) {
18+
@if (expanded) {
1919
<div
2020
role="region"
2121
[id]="accordionBodyId"
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,15 @@
11
import { _IdGenerator } from '@angular/cdk/a11y';
2+
import { CdkAccordionItem } from '@angular/cdk/accordion';
23
import {
34
ChangeDetectionStrategy,
45
Component,
5-
forwardRef,
66
inject,
77
input,
8-
signal,
98
} from '@angular/core';
109
import {
1110
COLLAPSE_ON_LEAVE,
1211
EXPAND_ON_ENTER_ANIMATION,
1312
} from './accordion-animations';
14-
import { AccordionComponent } from './accordion.component';
1513
import { ExpandIconComponent } from './expand-icon.component';
1614

1715
@Component({
@@ -25,31 +23,8 @@ import { ExpandIconComponent } from './expand-icon.component';
2523
class: 'block mb-3 pb-3 border-b border-divider',
2624
},
2725
})
28-
export class AccordionItemComponent {
29-
disabled = input<boolean>();
26+
export class AccordionItemComponent extends CdkAccordionItem {
3027
label = input<string>();
31-
accordion = inject<AccordionComponent>(forwardRef(() => AccordionComponent));
3228
accordionBodyId = inject(_IdGenerator).getId('accordion-item-body-');
3329
accordionTriggerId = inject(_IdGenerator).getId('accordion-item-trigger-');
34-
35-
expanded = input<boolean, boolean>(false, {
36-
transform: (value) => {
37-
if (value) {
38-
this.accordion.open(this);
39-
} else {
40-
this.accordion.close(this);
41-
}
42-
return value;
43-
},
44-
});
45-
isOpen = signal(false);
46-
47-
toggle() {
48-
const isOpen = this.isOpen();
49-
if (isOpen) {
50-
this.accordion.close(this);
51-
} else {
52-
this.accordion.open(this);
53-
}
54-
}
5530
}

projects/ngverse/src/lib/accordion/accordion.component.spec.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,8 @@ describe('AccordionComponent', () => {
4141
const firstAccordion = accordionItems[0].componentInstance;
4242
const secondAccordion = accordionItems[1].componentInstance;
4343
firstAccordion.toggle();
44-
expect(firstAccordion.isOpen()).toBeTrue();
45-
expect(secondAccordion.isOpen()).toBeFalse();
44+
expect(firstAccordion.expanded).toBeTrue();
45+
expect(secondAccordion.expanded).toBeFalse();
4646
});
4747
it('open accordion should not close another on multi=true', async () => {
4848
fixture.componentInstance.multi.set(true);
@@ -54,8 +54,8 @@ describe('AccordionComponent', () => {
5454
firstAccordion.toggle();
5555
await fixture.whenStable();
5656
secondAccordion.toggle();
57-
expect(firstAccordion.isOpen()).toBeTrue();
58-
expect(secondAccordion.isOpen()).toBeTrue();
57+
expect(firstAccordion.expanded).toBeTrue();
58+
expect(secondAccordion.expanded).toBeTrue();
5959
});
6060
});
6161
@Component({

0 commit comments

Comments
 (0)