diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md
index 37876749657..bb8f41e03c1 100644
--- a/.github/CONTRIBUTING.md
+++ b/.github/CONTRIBUTING.md
@@ -153,6 +153,33 @@ If the bug fix or new feature development requires changes to released public AP
2. Add a `BREAKING CHANGE:` section to the commit message body or footer. See https://www.conventionalcommits.org
3. Check if the change can be migrated by `ng update` schematics and add to the project migrations. See [Update Migrations wiki](https://github.com/IgniteUI/igniteui-angular/wiki/Update-Migrations) for available functionality and instructions.
+## Deprecating selectors
+When deprecating selectors the following code should be placed inside `OnInit` method of the class the selector belongs to:
+`
+import { isDevMode } from '@angular/core';
+...
+constructor(..., private element: ElementRef) {}
+...
+if (isDevMode() && this.element.nativeElement.tagName === 'your deprecated selector in upper case') {
+ console.log('your deprecation message');
+}
+`
+
+Write migrations.
+
+## Deprecating methods
+When a method is deprecated a few steps have to be done:
+1. Add deprecation warning message by decorating the method with `@DeprecateMethod` decorator from `deprecateDecorators.ts` file.
+2. Ensure that the deprecated method is no longer used in IgniteUI for Angular codebase, samples and documentation snippets.
+3. Write migrations.
+
+## Deprecating class properties
+When a class property is deprecated a few steps have to be done:
+1. Add deprecation warning message by decorating the property with `@DeprecateProperty` decorator from `deprecateDecorators.ts` file.
+2. Ensure that the deprecated property is no longer used in IgniteUI for Angular codebase, samples and documentation snippets.
+3. Write migrations.
+
+NOTE: TypeScript disallows decorating both the get and set accessor for a single member. Instead, all decorators for the member must be applied to the first accessor specified in document order. This is because decorators apply to a Property Descriptor, which combines both the get and set accessor, not each declaration separately.
# Testing a PR
In order to test a pull request that is awaiting test, perform the following actions.
diff --git a/projects/igniteui-angular/src/lib/core/deprecateDecorators.ts b/projects/igniteui-angular/src/lib/core/deprecateDecorators.ts
index 8273d65ec9e..c83b071d76d 100644
--- a/projects/igniteui-angular/src/lib/core/deprecateDecorators.ts
+++ b/projects/igniteui-angular/src/lib/core/deprecateDecorators.ts
@@ -1,27 +1,96 @@
+import { isDevMode } from '@angular/core';
/**
* @hidden
*/
-export function DeprecateClass(message: string): ClassDecorator {
- return (constructor: any) => {
- console.warn(constructor.name + ': ' + message);
+export function DeprecateMethod(message: string): MethodDecorator {
+ let isMessageShown = false;
+
+ return function (target: any, key: string, descriptor: PropertyDescriptor) {
+ if (descriptor && descriptor.value) {
+ const originalMethod = descriptor.value;
+
+ descriptor.value = function () {
+ const targetName = typeof target === 'function' ? target.name : target.constructor.name;
+ isMessageShown = showMessage(`${targetName}.${key}: ${message}`, isMessageShown);
+
+ return originalMethod.call(this, arguments);
+ };
+
+ return descriptor;
+ }
};
}
/**
* @hidden
*/
-export function DeprecateMethod(message: string): MethodDecorator {
- return (constructor: any) => {
- console.warn(constructor.constructor.name + ': ' + message);
+export function DeprecateProperty(message: string): PropertyDecorator {
+ return function(target: any, key: string) {
+ let isMessageShown = false;
+ const messageToDisplay = `${target.constructor.name}.${key}: ${message}`;
+
+ // if the target already has the property defined
+ const originalDescriptor = Object.getOwnPropertyDescriptor(target, key);
+ if (originalDescriptor) {
+ let getter, setter;
+ getter = originalDescriptor.get;
+ setter = originalDescriptor.set;
+
+ if (getter) {
+ originalDescriptor.get = function() {
+ isMessageShown = showMessage(messageToDisplay, isMessageShown);
+ return getter.call(this);
+ };
+ }
+
+ if (setter) {
+ originalDescriptor.set = function (value) {
+ isMessageShown = showMessage(messageToDisplay, isMessageShown);
+ setter.call(this, value);
+ };
+ }
+
+ return originalDescriptor;
+ }
+
+ // the target doesn't contain a descriptor for that property, so create one
+ // use backing field to set/get the value of the property to ensure there won't be infinite recursive calls
+ const newKey = generateUniqueKey(target, key);
+ return Object.defineProperty(target, key, {
+ configurable: true,
+ enumerable: true,
+ set: function(value) {
+ isMessageShown = showMessage(messageToDisplay, isMessageShown);
+ this[newKey] = value;
+ },
+ get: function() {
+ isMessageShown = showMessage(messageToDisplay, isMessageShown);
+ return this[newKey];
+ }
+ });
};
}
/**
* @hidden
*/
-export function DeprecateProperty(message: string): PropertyDecorator {
- return (constructor: any) => {
- console.warn(constructor.constructor.name + ': ' + message);
- };
+function generateUniqueKey(target: any, key: string): string {
+ let newKey = '_' + key;
+ while (target.hasOwnProperty(newKey)) {
+ newKey = '_' + newKey;
+ }
+
+ return newKey;
+}
+
+/**
+ * @hidden
+ */
+function showMessage(message: string, isMessageShown: boolean): boolean {
+ if (!isMessageShown && isDevMode()) {
+ console.warn(message);
+ }
+
+ return true;
}
diff --git a/projects/igniteui-angular/src/lib/date-picker/README.md b/projects/igniteui-angular/src/lib/date-picker/README.md
index 9e57586428f..b2bf055892d 100644
--- a/projects/igniteui-angular/src/lib/date-picker/README.md
+++ b/projects/igniteui-angular/src/lib/date-picker/README.md
@@ -61,14 +61,14 @@ The DatePicker also supports binding through `ngModel` if two-way date-bind is n
The DatePicker input group could be retemplated.
```html
-
Detailed description to be added.
Detailed description to be added.