Skip to content

Commit

Permalink
feat(core): Add viewportManager to orchestrate viewport syncing withi…
Browse files Browse the repository at this point in the history
…n groups
  • Loading branch information
diehbria committed Sep 12, 2022
1 parent aabd575 commit 9afdf26
Show file tree
Hide file tree
Showing 2 changed files with 129 additions and 0 deletions.
73 changes: 73 additions & 0 deletions packages/core/src/viewportManager/viewportManager.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { viewportManager } from './viewportManager';

beforeEach(() => {
viewportManager.reset();
});

const VIEWPORT = { duration: '2m' };

it('subscribes to a new viewport group and returned an undefined viewport', () => {
const { viewport } = viewportManager.subscribe('some-group', () => {});
expect(viewport).toBeUndefined();
});

it('broadcast updates to viewport group', () => {
const listener = jest.fn();
viewportManager.subscribe('some-group', listener);
viewportManager.update('some-group', VIEWPORT);
expect(listener).toHaveBeenLastCalledWith(VIEWPORT);
});

it('returns current viewport for group is returned upon initial subscription', () => {
const listener = jest.fn();
viewportManager.update('some-group', VIEWPORT);
const { viewport } = viewportManager.subscribe('some-group', listener);

expect(viewport).toBe(VIEWPORT);
});

it('returns no viewport is returned on initial subscription when reset is called', () => {
const listener = jest.fn();
viewportManager.update('some-group', VIEWPORT);
viewportManager.reset();
const { viewport } = viewportManager.subscribe('some-group', listener);

expect(viewport).toBeUndefined();
});

it('does not broadcast viewport updates to different viewport groups ', () => {
const listener = jest.fn();
viewportManager.subscribe('some-group', listener);
viewportManager.update('some-other-group', VIEWPORT);
expect(listener).not.toHaveBeenCalled();
});

it('broadcasts viewports to multiple listeners', () => {
const listener = jest.fn();
const listener2 = jest.fn();
viewportManager.subscribe('some-group', listener);
viewportManager.subscribe('some-group', listener2);
viewportManager.update('some-group', VIEWPORT);

expect(listener).toHaveBeenLastCalledWith(VIEWPORT);
expect(listener2).toHaveBeenLastCalledWith(VIEWPORT);
});

it('does not broadcast updates to a unsubscribed listener', () => {
const listener = jest.fn();
const { unsubscribe } = viewportManager.subscribe('some-group', listener);

unsubscribe();
viewportManager.update('some-group', VIEWPORT);
expect(listener).not.toHaveBeenCalled();
});

it('does not broadcast updates to a listener after reset is called', () => {
const listener = jest.fn();
viewportManager.subscribe('some-group', listener);

viewportManager.reset();
viewportManager.update('some-group', VIEWPORT);

expect(listener).not.toHaveBeenCalled();
});
56 changes: 56 additions & 0 deletions packages/core/src/viewportManager/viewportManager.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { MinimalViewPortConfig } from '@synchro-charts/core';
import { v4 } from 'uuid';

type ViewportListener = (viewport: MinimalViewPortConfig) => void;

let listenerMap: { [group: string]: { [id: string]: ViewportListener } } = {};
let viewportMap: { [group: string]: MinimalViewPortConfig } = {};
/**
* Publicly exposed manager of viewport groups. Allows components, both internally to IoT App Kit,
* and external components / code to broadcast updates to viewports within a group.
*
* Utilized to allow widgets to provide a synchronized view into data - an example can be
* found at https://synchrocharts.com/#/Features/Synchronization
*/
export const viewportManager = {
/**
* Resets all state related to viewport groups.
*/
reset: () => {
listenerMap = {};
viewportMap = {};
},
/**
* Subscribe to viewport group
*
* @param viewportGroup - group to subscribe to
* @param viewportListener - listener for viewport group updates. Called every time an update to the group is called. Not called upon initial subscription
*/
subscribe: (
viewportGroup: string,
viewportListener: (viewport: MinimalViewPortConfig) => void
): {
unsubscribe: () => void;
viewport: MinimalViewPortConfig | null;
} => {
const id = v4();
if (listenerMap[viewportGroup] == null) {
listenerMap[viewportGroup] = {};
}
listenerMap[viewportGroup][id] = viewportListener;

return {
// Current viewport for the group
viewport: viewportMap[viewportGroup],
// Leave viewport group, prevents listener from being called in the future
unsubscribe: () => {
delete listenerMap[viewportGroup][id];
},
};
},
update: (viewportGroup: string, viewport: MinimalViewPortConfig): void => {
viewportMap[viewportGroup] = viewport;
// broadcast update to all listeners within the group
Object.keys(listenerMap[viewportGroup] || {}).forEach((id) => listenerMap[viewportGroup][id](viewport));
},
};

0 comments on commit 9afdf26

Please sign in to comment.