From 08421d37cfc95bc5bbc34d809937bbfa74ea10e8 Mon Sep 17 00:00:00 2001 From: Arman Ozak Date: Fri, 10 Apr 2020 00:11:41 +0300 Subject: [PATCH] feat: add context strategies --- .../src/lib/strategies/context.strategy.ts | 47 +++++++++++ .../packages/core/src/lib/strategies/index.ts | 1 + .../src/lib/tests/context.strategy.spec.ts | 79 +++++++++++++++++++ 3 files changed, 127 insertions(+) create mode 100644 npm/ng-packs/packages/core/src/lib/strategies/context.strategy.ts create mode 100644 npm/ng-packs/packages/core/src/lib/tests/context.strategy.spec.ts diff --git a/npm/ng-packs/packages/core/src/lib/strategies/context.strategy.ts b/npm/ng-packs/packages/core/src/lib/strategies/context.strategy.ts new file mode 100644 index 0000000000..e3d16c0573 --- /dev/null +++ b/npm/ng-packs/packages/core/src/lib/strategies/context.strategy.ts @@ -0,0 +1,47 @@ +import { ComponentRef, TemplateRef, Type } from '@angular/core'; +import { InferedContextOf, InferedInstanceOf } from '../models'; + +export abstract class ContextStrategy { + constructor(public context: Partial>) {} + + /* tslint:disable-next-line:no-unused-variable */ + setContext(componentRef?: ComponentRef>): Partial> { + return this.context; + } +} + +export class NoContextStrategy< + T extends Type | TemplateRef = any +> extends ContextStrategy { + constructor() { + super(undefined); + } +} + +export class ComponentContextStrategy = any> extends ContextStrategy { + setContext(componentRef: ComponentRef>): Partial> { + Object.keys(this.context).forEach(key => (componentRef.instance[key] = this.context[key])); + componentRef.changeDetectorRef.detectChanges(); + return this.context; + } +} + +export class TemplateContextStrategy = any> extends ContextStrategy { + setContext(): Partial> { + return this.context; + } +} + +export const CONTEXT_STRATEGY = { + None | TemplateRef = any>() { + return new NoContextStrategy(); + }, + Component = any>(context: Partial>) { + return new ComponentContextStrategy(context); + }, + Template = any>(context: Partial>) { + return new TemplateContextStrategy(context); + }, +}; + +type ContextType = T extends Type | TemplateRef ? U : never; diff --git a/npm/ng-packs/packages/core/src/lib/strategies/index.ts b/npm/ng-packs/packages/core/src/lib/strategies/index.ts index 2d6be484dd..28447748f7 100644 --- a/npm/ng-packs/packages/core/src/lib/strategies/index.ts +++ b/npm/ng-packs/packages/core/src/lib/strategies/index.ts @@ -1,5 +1,6 @@ export * from './content-security.strategy'; export * from './content.strategy'; +export * from './context.strategy'; export * from './cross-origin.strategy'; export * from './dom.strategy'; export * from './loading.strategy'; diff --git a/npm/ng-packs/packages/core/src/lib/tests/context.strategy.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/context.strategy.spec.ts new file mode 100644 index 0000000000..461c397457 --- /dev/null +++ b/npm/ng-packs/packages/core/src/lib/tests/context.strategy.spec.ts @@ -0,0 +1,79 @@ +import { ComponentRef } from '@angular/core'; +import { + ComponentContextStrategy, + CONTEXT_STRATEGY, + NoContextStrategy, + TemplateContextStrategy, +} from '../strategies'; +import { uuid } from '../utils'; + +describe('ComponentContextStrategy', () => { + describe('#setContext', () => { + let componentRef: ComponentRef; + + beforeEach( + () => + (componentRef = { + instance: { + x: '', + y: '', + z: '', + }, + changeDetectorRef: { + detectChanges: jest.fn(), + }, + } as any), + ); + + test.each` + props | values + ${['x']} | ${[uuid()]} + ${['x', 'y']} | ${[uuid(), uuid()]} + ${['x', 'y', 'z']} | ${[uuid(), uuid(), uuid()]} + `( + 'should set $props as $values and call detectChanges once', + ({ props, values }: { props: string[]; values: string[] }) => { + const context = {}; + props.forEach((prop, i) => { + context[prop] = values[i]; + }); + + const strategy = new ComponentContextStrategy(context); + strategy.setContext(componentRef); + + expect(props.every(prop => componentRef.instance[prop] === context[prop])).toBe(true); + expect(componentRef.changeDetectorRef.detectChanges).toHaveBeenCalledTimes(1); + }, + ); + }); +}); + +describe('NoContextStrategy', () => { + describe('#setContext', () => { + it('should return undefined', () => { + const strategy = new NoContextStrategy(); + expect(strategy.setContext(null)).toBeUndefined(); + }); + }); +}); + +describe('TemplateContextStrategy', () => { + describe('#setContext', () => { + it('should return context', () => { + const context = { x: uuid() }; + const strategy = new TemplateContextStrategy(context); + expect(strategy.setContext()).toEqual(context); + }); + }); +}); + +describe('CONTEXT_STRATEGY', () => { + test.each` + name | Strategy + ${'Component'} | ${ComponentContextStrategy} + ${'None'} | ${NoContextStrategy} + ${'Template'} | ${TemplateContextStrategy} + `('should successfully map $name to $Strategy.name', ({ name, Strategy }) => { + expect(CONTEXT_STRATEGY[name](undefined)).toEqual(new Strategy(undefined)); + }); +});