diff --git a/src/WidgetBase.ts b/src/WidgetBase.ts index 8e3a0914..538b79b6 100644 --- a/src/WidgetBase.ts +++ b/src/WidgetBase.ts @@ -7,6 +7,7 @@ import { isWNode, v, isHNode } from './d'; import { auto, ignore } from './diff'; import { AfterRender, + BeforeProperties, BeforeRender, CoreProperties, DiffPropertyFunction, @@ -361,7 +362,8 @@ export class WidgetBase

extends E this._coreProperties = coreProperties; } - public __setProperties__(properties: this['properties']): void { + public __setProperties__(originalProperties: this['properties']): void { + const properties = this._runBeforeProperties(originalProperties); const changedPropertyKeys: string[] = []; const allProperties = [ ...Object.keys(properties), ...Object.keys(this._properties) ]; const checkedProperties: string[] = []; @@ -378,7 +380,7 @@ export class WidgetBase

extends E } checkedProperties.push(propertyName); const previousProperty = this._properties[propertyName]; - const newProperty = this._bindFunctionProperty((properties as any)[propertyName], this._coreProperties.bind); + const newProperty = this._bindFunctionProperty(properties[propertyName], this._coreProperties.bind); if (registeredDiffPropertyNames.indexOf(propertyName) !== -1) { runReactions = true; const diffFunctions = this.getDecorator(`diffProperty:${propertyName}`); @@ -633,6 +635,16 @@ export class WidgetBase

extends E return this._registries; } + private _runBeforeProperties(properties: any) { + const beforeProperties: BeforeProperties[] = this.getDecorator('beforeProperties'); + if (beforeProperties.length > 0) { + return beforeProperties.reduce((properties, beforePropertiesFunction) => { + return { ...properties, ...beforePropertiesFunction(properties) }; + }, { ...properties }); + } + return properties; + } + /** * Run all registered before renders and return the updated render method */ diff --git a/src/decorators/beforeProperties.ts b/src/decorators/beforeProperties.ts new file mode 100644 index 00000000..4561d450 --- /dev/null +++ b/src/decorators/beforeProperties.ts @@ -0,0 +1,16 @@ +import { handleDecorator } from './../WidgetBase'; +import { BeforeProperties } from './../interfaces'; + +/** + * Decorator that adds the function passed of target method to be run + * in the `beforeProperties` lifecycle. + */ +export function beforeProperties(method: BeforeProperties): (target: any) => void; +export function beforeProperties(): (target: any, propertyKey: string) => void; +export function beforeProperties(method?: BeforeProperties) { + return handleDecorator((target, propertyKey) => { + target.addDecorator('beforeProperties', propertyKey ? target[propertyKey] : method); + }); +} + +export default beforeProperties; diff --git a/src/interfaces.d.ts b/src/interfaces.d.ts index c72ce8a5..66f514d7 100644 --- a/src/interfaces.d.ts +++ b/src/interfaces.d.ts @@ -442,3 +442,10 @@ export interface BeforeRender { export interface AfterRender { (dNode: DNode | DNode []): DNode | DNode[]; } + +/** + * Interface for beforeProperties function + */ +export interface BeforeProperties

{ + (properties: P): P; +} diff --git a/tests/unit/all.ts b/tests/unit/all.ts index 3c558758..d7acfc6e 100644 --- a/tests/unit/all.ts +++ b/tests/unit/all.ts @@ -5,6 +5,7 @@ import './WidgetRegistry'; import './lifecycle'; import './customElements'; import './d'; +import './decorators/all'; import './mixins/all'; import './util/all'; import './main'; diff --git a/tests/unit/decorators/all.ts b/tests/unit/decorators/all.ts new file mode 100644 index 00000000..f8ac3f2d --- /dev/null +++ b/tests/unit/decorators/all.ts @@ -0,0 +1 @@ +import './beforeProperties'; diff --git a/tests/unit/decorators/beforeProperties.ts b/tests/unit/decorators/beforeProperties.ts new file mode 100644 index 00000000..fca8923d --- /dev/null +++ b/tests/unit/decorators/beforeProperties.ts @@ -0,0 +1,69 @@ +import * as registerSuite from 'intern!object'; +import * as assert from 'intern/chai!assert'; + +import { beforeProperties } from './../../../src/decorators/beforeProperties'; +import { WidgetBase } from './../../../src/WidgetBase'; +import { WidgetProperties } from './../../../src/interfaces'; + +registerSuite({ + name: 'decorators/beforeProperties', + beforeProperties() { + function before(properties: WidgetProperties): WidgetProperties { + return { key: 'foo' }; + } + + @beforeProperties(before) + class TestWidget extends WidgetBase {} + const widget = new TestWidget(); + widget.__setProperties__({}); + assert.strictEqual(widget.properties.key, 'foo'); + }, + 'multiple beforeProperties decorators'() { + function beforeOne(properties: WidgetProperties): WidgetProperties { + return { key: 'foo' }; + } + function beforeTwo(properties: any): any { + return { other: 'bar' }; + } + + @beforeProperties(beforeOne) + @beforeProperties(beforeTwo) + class TestWidget extends WidgetBase {} + const widget = new TestWidget(); + widget.__setProperties__({}); + assert.strictEqual(widget.properties.key, 'foo'); + assert.strictEqual(widget.properties.other, 'bar'); + }, + 'beforeProperties on class method'() { + class TestWidget extends WidgetBase { + @beforeProperties() + before(properties: WidgetProperties): WidgetProperties { + return { key: 'foo' }; + } + } + const widget = new TestWidget(); + widget.__setProperties__({}); + assert.strictEqual(widget.properties.key, 'foo'); + + }, + 'programmatic beforeProperties'() { + function beforeOne(properties: WidgetProperties): WidgetProperties { + return { key: 'foo' }; + } + function beforeTwo(properties: any): any { + return { other: 'bar' }; + } + + class TestWidget extends WidgetBase { + constructor() { + super(); + beforeProperties(beforeOne)(this); + beforeProperties(beforeTwo)(this); + } + } + const widget = new TestWidget(); + widget.__setProperties__({}); + assert.strictEqual(widget.properties.key, 'foo'); + assert.strictEqual(widget.properties.other, 'bar'); + } +});