Skip to content

Commit

Permalink
feat: override and readonly decorators deprecate and replaced with a …
Browse files Browse the repository at this point in the history
…`prop` decorator
  • Loading branch information
ala-n committed Apr 13, 2021
1 parent a1def0f commit eb4040e
Show file tree
Hide file tree
Showing 8 changed files with 87 additions and 105 deletions.
8 changes: 3 additions & 5 deletions src/modules/esl-forms/esl-select/core/esl-select-dropdown.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {ESLToggleable, ToggleableActionParams} from '../../../esl-toggleable/core/esl-toggleable';
import {bind} from '../../../esl-utils/decorators/bind';
import {override} from '../../../esl-utils/decorators/override';
import {prop} from '../../../esl-utils/decorators/prop';
import {TAB} from '../../../esl-utils/dom/keys';
import {rafDecorator} from '../../../esl-utils/async/raf';
import {ESLSelectList} from '../../esl-select-list/core';
Expand Down Expand Up @@ -29,10 +29,8 @@ export class ESLSelectDropdown extends ESLToggleable {
protected _disposeTimeout: number;
protected _deferredUpdatePosition = rafDecorator(() => this.updatePosition());

@override()
public closeOnEsc = true;
@override()
public closeOnOutsideAction = true;
@prop() public closeOnEsc = true;
@prop() public closeOnOutsideAction = true;

constructor() {
super();
Expand Down
4 changes: 1 addition & 3 deletions src/modules/esl-utils/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,7 @@ Even if you use the whole utils module, it is still tiny.

- #### [@memoize](./decorators/memoize.ts) - TS decorator to make method or get accessor memoized.

- #### [@override](./decorators/override.ts) - TS decorator to define a writable field on the prototype level.

- #### [@constant](./decorators/readonly.ts) - TS decorator to create o readonly field on the prototype level.
- #### [@prop](src/modules/esl-utils/decorators/prop.ts) - TS decorator to define a field on the prototype level.

- ### DOM Helpers

Expand Down
3 changes: 1 addition & 2 deletions src/modules/esl-utils/all.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,7 @@ export * from './abstract/observable';
// Decorators
export * from './decorators/bind';
export * from './decorators/memoize';
export * from './decorators/override';
export * from './decorators/readonly';
export * from './decorators/prop';

// Function
export * from './async/promise';
Expand Down
2 changes: 2 additions & 0 deletions src/modules/esl-utils/decorators/override.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
/**
* @deprecated This decorator is deprecated use {@link property} decorator instead.
*
* `@override` is auxiliary decorator to override a field that is decorated in the parent class.
* The override _value_ is always defined on the object prototype level.
*
Expand Down
38 changes: 38 additions & 0 deletions src/modules/esl-utils/decorators/prop.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/** Property configuration */
export type OverrideDecoratorConfig = {
/** Value to set in the prototype */
value?: any;
/** To define mutable property */
readonly?: false;
} | {
/** Value to set in the prototype */
value: any;
/** To define readonly property */
readonly: true;
};

/**
* `@prop` is auxiliary decorator to define a field on the prototype level.
*` @prop` can be used to override decorated property from the parent level
*
* You can also use an @override decorator in combination with ECMA Script class property definition:
* `@prop() public field: any = initial value;`
*
* The class property initial value is a part of object creation, so it goes to the object itself,
* while the @override value is defined on the prototype level.
*
* @param [prototypeConfig] - prototype property configuration
*/
export function prop(prototypeConfig: OverrideDecoratorConfig = {}) {
return function (obj: any, name: string): void {
if (Object.hasOwnProperty.call(obj, name)) {
throw new TypeError('Can\'t override own property');
}
Object.defineProperty(obj, name, {
value: prototypeConfig.value,
writable: !prototypeConfig.readonly,
enumerable: true,
configurable: true
});
};
}
6 changes: 4 additions & 2 deletions src/modules/esl-utils/decorators/readonly.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
/**
* @deprecated This property is deprecated use @property instead
*
* `@readonly` is auxiliary decorator to create a readonly field on prototype level.
*
* @param value - property value
* @param [silent] - to not throw error on setting value
* @param value - property value
* @param [silent] - to not throw error on setting value
*/
export function readonly(value: any, silent = false) {
return function (obj: any, name: string): void {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import '../../../../polyfills/es5-target-shim';
import {ESLBaseElement, attr, boolAttr, jsonAttr} from '../../../esl-base-element/core';
import {override} from '../override';
import {prop} from '../prop';

describe('Decorator: override', () => {
describe('Decorator: @prop', () => {
class TestBaseElement extends ESLBaseElement {
@attr()
public field: string;
Expand All @@ -18,11 +18,11 @@ describe('Decorator: override', () => {

describe('Overriding @attr', () => {
class TestElement extends TestBaseElement {
@override('test')
@prop({ value: 'test' })
public field: string;
@override()
@prop()
public field4?: string;
@override('test')
@prop({ value: 'test' })
public readonlyField: string;
}
customElements.define('attr-override-1', TestElement);
Expand Down Expand Up @@ -54,9 +54,38 @@ describe('Decorator: override', () => {
});
});

describe('Overriding with a non writable', () => {
class TestElement extends TestBaseElement {
@prop({ value: 'test', readonly: true }) public field: string;
@prop({ value: true, readonly: true }) public field2: boolean;
}
class TestElement2 extends TestElement {
@prop() public field: string = 'test2';
@prop({ value: false }) public field2: boolean;
}
customElements.define('attr-writable-override-1', TestElement);
customElements.define('attr-writable-override-2', TestElement2);

test('should override @attr and @boolAttr decorator', () => {
const el = new TestElement();
expect(el.field).toBe('test');
expect(el.field2).toBe(true);
});
test('override should not be writeable', () => {
const el = new TestElement();
expect(() => el.field = 'hi').toThrowError();
expect(() => el.field2 = false).toThrowError();
});
test('should have undefined as a default', () => {
const el = new TestElement2();
expect(el.field).toBe('test2');
expect(el.field2).toBe(false);
});
});

describe('Overriding @boolAttr', () => {
class TestElement extends TestBaseElement {
@override(true)
@prop({ value: true })
public field2: boolean;
}
customElements.define('bool-attr-override-1', TestElement);
Expand All @@ -78,7 +107,7 @@ describe('Decorator: override', () => {

describe('Overriding @jsonAttr', () => {
class TestElement extends TestBaseElement {
@override({a: 2})
@prop({ value: {a: 2} })
public field3: {a: number};
}
customElements.define('json-attr-override-1', TestElement);
Expand All @@ -100,7 +129,7 @@ describe('Decorator: override', () => {

describe('Overridden property can be defined through ES initial value ', () => {
class TestElement extends TestBaseElement {
@override()
@prop()
public field: string = '123';
}
customElements.define('es-initial-attr-override-1', TestElement);
Expand All @@ -114,7 +143,7 @@ describe('Decorator: override', () => {
test('Overriding own property produce error', () => {
expect(() => {
class TestElement extends ESLBaseElement {
@override('')
@prop({ value: '' })
@attr()
public field: string;
}
Expand Down
84 changes: 0 additions & 84 deletions src/modules/esl-utils/decorators/test/readonly.test.ts

This file was deleted.

0 comments on commit eb4040e

Please sign in to comment.