Skip to content

Commit

Permalink
feat(daffio): add sidebar service (#3029)
Browse files Browse the repository at this point in the history
  • Loading branch information
griest024 authored Aug 23, 2024
1 parent 2960d51 commit 8273ef1
Show file tree
Hide file tree
Showing 2 changed files with 198 additions and 0 deletions.
124 changes: 124 additions & 0 deletions apps/daffio/src/app/core/sidebar/services/sidebar.service.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
import {
BreakpointObserver,
BreakpointState,
} from '@angular/cdk/layout';
import { TestBed } from '@angular/core/testing';
import { cold } from 'jasmine-marbles';
import { BehaviorSubject } from 'rxjs';

import {
DaffSidebarModeEnum,
DaffSidebarRegistration,
} from '@daffodil/design/sidebar';
import { DaffRouterDataService } from '@daffodil/router';

import { DaffioSidebarService } from './sidebar.service';
import { DAFFIO_NAV_LINKS_SIDEBAR_ID } from '../../nav/sidebar.provider';
import { DaffioRoute } from '../../router/route.type';


describe('DaffioSidebarService', () => {
let service: DaffioSidebarService;
let dataSpy: BehaviorSubject<DaffioRoute['data']>;
let breakpointSpy: BehaviorSubject<BreakpointState>;

beforeEach(() => {
dataSpy = new BehaviorSubject({});
breakpointSpy = new BehaviorSubject({
matches: false,
breakpoints: {},
});

TestBed.configureTestingModule({
providers: [
{
provide: DaffRouterDataService,
useValue: jasmine.createSpyObj('DaffRouterDataService', [], { data$: dataSpy }),
},
{
provide: BreakpointObserver,
useValue: jasmine.createSpyObj('BreakpintObserver', { observe: breakpointSpy }),
},
],
});

service = TestBed.inject(DaffioSidebarService);
});

describe('mode$', () => {
describe('in big tablet mode', () => {
beforeEach(() => {
breakpointSpy.next({ matches: true, breakpoints: {}});
});

it('should pull sidebar mode from router data', () => {
dataSpy.next({
sidebarMode: DaffSidebarModeEnum.Side,
});
expect(service.mode$).toBeObservable(cold('a', { a: DaffSidebarModeEnum.Side }));
});

it('should default to side-fixed', () => {
dataSpy.next({});
expect(service.mode$).toBeObservable(cold('a', { a: DaffSidebarModeEnum.SideFixed }));
});
});

describe('in smaller than big tablet', () => {
beforeEach(() => {
breakpointSpy.next({ matches: false, breakpoints: {}});
});

it('should be under', () => {
expect(service.mode$).toBeObservable(cold('a', { a: DaffSidebarModeEnum.Under }));
});
});
});

describe('activeRegistration$', () => {
let testRegistration: DaffSidebarRegistration;

beforeEach(() => {
testRegistration = {
id: DAFFIO_NAV_LINKS_SIDEBAR_ID,
};
dataSpy.next({
daffioSidebars: {
[DAFFIO_NAV_LINKS_SIDEBAR_ID]: testRegistration,
},
});
});

it('should default to the nav links registration', () => {
expect(service.activeRegistration$).toBeObservable(cold('a', { a: testRegistration }));
});

describe('when an unknown sidebar is opened', () => {
beforeEach(() => {
service.open('id');
});

it('should not return a registration', () => {
expect(service.activeRegistration$).toBeObservable(cold('a', { a: undefined }));
});
});

describe('when a known sidebar is opened', () => {
beforeEach(() => {
testRegistration = {
id: 'id',
};
dataSpy.next({
daffioSidebars: {
id: testRegistration,
},
});
service.open(testRegistration.id);
});

it('should return the registration', () => {
expect(service.activeRegistration$).toBeObservable(cold('a', { a: testRegistration }));
});
});
});
});
74 changes: 74 additions & 0 deletions apps/daffio/src/app/core/sidebar/services/sidebar.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { BreakpointObserver } from '@angular/cdk/layout';
import {
Inject,
Injectable,
} from '@angular/core';
import {
combineLatest,
distinctUntilChanged,
filter,
map,
} from 'rxjs';

import {
DaffBreakpoints,
SERVER_SAFE_BREAKPOINT_OBSERVER,
} from '@daffodil/design';
import {
DaffSidebarModeEnum,
DaffSidebarService,
} from '@daffodil/design/sidebar';
import { DaffRouterDataService } from '@daffodil/router';

import { DAFFIO_NAV_LINKS_SIDEBAR_ID } from '../../nav/sidebar.provider';
import { DaffioRoute } from '../../router/route.type';

/**
* A sidebar service that handles the mode logic and pulling the active registration from {@link DaffioRouteWithSidebars#data#daffioSidebars}.
*/
@Injectable({
providedIn: 'root',
})
export class DaffioSidebarService extends DaffSidebarService {
/**
* The sidebar mode.
* In big tablet, it will use the sidebar mode from {@link DaffioRouteWithSidebars#data#sidebarMode}, if defined, side-fixed otherwise.
* In a viewport smaller than big tablet it will be under.
*/
readonly mode$ = combineLatest([
this.routerData.data$,
this.breakpointObserver.observe(DaffBreakpoints.BIG_TABLET).pipe(
map((result) => result?.matches),
),
]).pipe(
distinctUntilChanged(),
map(([data, isBigTablet]) =>
isBigTablet
? data.sidebarMode || DaffSidebarModeEnum.SideFixed
: DaffSidebarModeEnum.Under,
),
distinctUntilChanged(),
);

/**
* The sidebar registration currently activated by `open` or {@link DAFFIO_NAV_LINKS_SIDEBAR_ID}.
*/
readonly activeRegistration$ = combineLatest([
this.id$.pipe(
filter((id) => !!id),
),
this.routerData.data$.pipe(
map((data) => data.daffioSidebars),
),
]).pipe(
distinctUntilChanged(),
map(([id, sidebars]) => sidebars[id]),
);

constructor(
@Inject(SERVER_SAFE_BREAKPOINT_OBSERVER) private breakpointObserver: BreakpointObserver,
private routerData: DaffRouterDataService<DaffioRoute['data']>,
) {
super(DAFFIO_NAV_LINKS_SIDEBAR_ID);
}
}

0 comments on commit 8273ef1

Please sign in to comment.