Skip to content

Commit 35eda07

Browse files
authored
feat(design): add sidebar service (#3014)
1 parent d2567fa commit 35eda07

File tree

4 files changed

+130
-0
lines changed

4 files changed

+130
-0
lines changed

libs/design/sidebar/src/public_api.ts

+2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
export { DaffSidebarModule } from './sidebar.module';
2+
export * from './service/registration.type';
3+
export * from './service/sidebar.service';
24
export * from './sidebar-viewport/sidebar-viewport.component';
35
export * from './sidebar/sidebar.component';
46
export * from './sidebar-header/sidebar-header.component';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { Type } from '@angular/core';
2+
3+
/**
4+
* Represents a registration of a sidebar.
5+
* A collection of sidebar components is associated with an ID which can be passed to {@link DaffSidebarService#open}.
6+
*/
7+
export interface DaffSidebarRegistration {
8+
id: string;
9+
header?: Type<unknown>;
10+
body?: Type<unknown>;
11+
footer?: Type<unknown>;
12+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import { Injectable } from '@angular/core';
2+
import { TestBed } from '@angular/core/testing';
3+
import { take } from 'rxjs';
4+
5+
import { DaffSidebarService } from './sidebar.service';
6+
7+
@Injectable({
8+
providedIn: 'root',
9+
})
10+
class TestService extends DaffSidebarService {
11+
constructor() {
12+
super('defaultId');
13+
}
14+
}
15+
16+
describe('@daffodil/design/sidebar | DaffSidebarService', () => {
17+
let service: TestService;
18+
19+
beforeEach(() => {
20+
service = TestBed.inject(TestService);
21+
});
22+
23+
it('should init id to the default ID', (done) => {
24+
service.id$.pipe(
25+
take(1),
26+
).subscribe((id) => {
27+
expect(id).toEqual('defaultId');
28+
done();
29+
});
30+
});
31+
32+
describe('open', () => {
33+
let id: string;
34+
35+
beforeEach(() => {
36+
id = 'id';
37+
service.open(id);
38+
});
39+
40+
it('should set the ID', (done) => {
41+
service.id$.pipe(
42+
take(1),
43+
).subscribe((v) => {
44+
expect(v).toEqual(id);
45+
done();
46+
});
47+
});
48+
49+
it('should set isOpen to true', () => {
50+
expect(service.isOpen()).toBeTrue();
51+
});
52+
});
53+
54+
describe('close', () => {
55+
beforeEach(() => {
56+
service.close();
57+
});
58+
59+
it('should set isOpen to false', () => {
60+
expect(service.isOpen()).toBeFalse();
61+
});
62+
});
63+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import {
2+
Signal,
3+
signal,
4+
} from '@angular/core';
5+
import {
6+
BehaviorSubject,
7+
Observable,
8+
} from 'rxjs';
9+
10+
import { DaffSidebarRegistration } from './registration.type';
11+
12+
/**
13+
* A service that stores the open state and ID of the currently opened sidebar.
14+
*
15+
* A default sidebar ID can be passed to the constructor that will be the initial value of `$id`.
16+
*/
17+
export abstract class DaffSidebarService {
18+
protected _id$: BehaviorSubject<DaffSidebarRegistration['id']>;
19+
protected _open = signal(false);
20+
21+
readonly id$: Observable<DaffSidebarRegistration['id']>;
22+
23+
get isOpen(): Signal<boolean> {
24+
return this._open.asReadonly();
25+
}
26+
27+
constructor(
28+
defaultId: DaffSidebarRegistration['id'] = null,
29+
) {
30+
this._id$ = new BehaviorSubject<DaffSidebarRegistration['id']>(defaultId);
31+
32+
this.id$ = this._id$.asObservable();
33+
}
34+
35+
/**
36+
* Opens the specified sidebar.
37+
*
38+
* @param id The optional sidebar ID. If omitted the most recently passed opened sidebar ID will persist (or the default if none was passed).
39+
*/
40+
open(id?: DaffSidebarRegistration['id']) {
41+
if (id) {
42+
this._id$.next(id);
43+
}
44+
this._open.set(true);
45+
};
46+
47+
/**
48+
* Closes the sidebar. Does not clear the ID.
49+
*/
50+
close() {
51+
this._open.set(false);
52+
}
53+
}

0 commit comments

Comments
 (0)