Browse Source

Merge pull request #1826 from abpframework/feature/tests

refactor: fix all lint errors and update tslint.json
pull/1834/head
Yasin Aydın 7 years ago
committed by GitHub
parent
commit
1fbf4f0b9d
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 3
      npm/ng-packs/.prettierrc
  2. 45
      npm/ng-packs/angular.json
  3. 70
      npm/ng-packs/package.json
  4. 33
      npm/ng-packs/packages/account/src/lib/components/tenant-box/tenant-box.component.ts
  5. 8
      npm/ng-packs/packages/account/src/lib/services/account.service.ts
  6. 8
      npm/ng-packs/packages/core/src/lib/abstracts/ng-model.component.ts
  7. 8
      npm/ng-packs/packages/core/src/lib/components/dynamic-layout.component.ts
  8. 5
      npm/ng-packs/packages/core/src/lib/directives/autofocus.directive.ts
  9. 9
      npm/ng-packs/packages/core/src/lib/directives/debounce.directive.ts
  10. 11
      npm/ng-packs/packages/core/src/lib/directives/for.directive.ts
  11. 16
      npm/ng-packs/packages/core/src/lib/directives/form-submit.directive.ts
  12. 5
      npm/ng-packs/packages/core/src/lib/directives/stop-propagation.directive.ts
  13. 10
      npm/ng-packs/packages/core/src/lib/directives/visibility.directive.ts
  14. 2
      npm/ng-packs/packages/core/src/lib/pipes/index.ts
  15. 11
      npm/ng-packs/packages/core/src/lib/pipes/localization.pipe.ts
  16. 17
      npm/ng-packs/packages/core/src/lib/pipes/sort.pipe.ts
  17. 20
      npm/ng-packs/packages/core/src/lib/plugins/config/config.plugin.ts
  18. 46
      npm/ng-packs/packages/core/src/lib/states/config.state.ts
  19. 154
      npm/ng-packs/packages/core/src/lib/tests/dynamic-layout.component.spec.ts
  20. 15
      npm/ng-packs/packages/core/src/lib/tests/router-outlet.component.spec.ts
  21. 1
      npm/ng-packs/packages/core/src/lib/utils/common-utils.ts
  22. 3
      npm/ng-packs/packages/core/src/lib/utils/generator-utils.ts
  23. 4
      npm/ng-packs/packages/core/src/lib/utils/initial-utils.ts
  24. 5
      npm/ng-packs/packages/core/src/lib/utils/rxjs-utils.ts
  25. 20
      npm/ng-packs/packages/feature-management/src/lib/components/feature-management/feature-management.component.ts
  26. 2
      npm/ng-packs/packages/feature-management/src/lib/states/index.ts
  27. 20
      npm/ng-packs/packages/identity/src/lib/components/roles/roles.component.ts
  28. 34
      npm/ng-packs/packages/identity/src/lib/components/users/users.component.ts
  29. 49
      npm/ng-packs/packages/permission-management/src/lib/components/permission-management.component.ts
  30. 2
      npm/ng-packs/packages/permission-management/src/lib/states/index.ts
  31. 40
      npm/ng-packs/packages/tenant-management/src/lib/components/tenants/tenants.component.ts
  32. 20
      npm/ng-packs/packages/tenant-management/src/lib/services/tenant-management.service.ts
  33. 29
      npm/ng-packs/packages/theme-basic/src/lib/components/application-layout/application-layout.component.ts
  34. 4
      npm/ng-packs/packages/theme-basic/src/lib/components/layout/layout.component.ts
  35. 2
      npm/ng-packs/packages/theme-basic/src/lib/constants/styles.ts
  36. 2
      npm/ng-packs/packages/theme-basic/src/lib/states/index.ts
  37. 20
      npm/ng-packs/packages/theme-basic/src/lib/theme-basic.module.ts
  38. 4
      npm/ng-packs/packages/theme-shared/package.json
  39. 8
      npm/ng-packs/packages/theme-shared/src/lib/abstracts/toaster.ts
  40. 10
      npm/ng-packs/packages/theme-shared/src/lib/animations/collapse.animations.ts
  41. 13
      npm/ng-packs/packages/theme-shared/src/lib/components/breadcrumb/breadcrumb.component.html
  42. 16
      npm/ng-packs/packages/theme-shared/src/lib/components/breadcrumb/breadcrumb.component.ts
  43. 10
      npm/ng-packs/packages/theme-shared/src/lib/components/button/button.component.ts
  44. 27
      npm/ng-packs/packages/theme-shared/src/lib/components/change-password/change-password.component.ts
  45. 26
      npm/ng-packs/packages/theme-shared/src/lib/components/chart/chart.component.ts
  46. 12
      npm/ng-packs/packages/theme-shared/src/lib/components/confirmation/confirmation.component.ts
  47. 22
      npm/ng-packs/packages/theme-shared/src/lib/components/error/error.component.html
  48. 0
      npm/ng-packs/packages/theme-shared/src/lib/components/error/error.component.scss
  49. 22
      npm/ng-packs/packages/theme-shared/src/lib/components/error/error.component.ts
  50. 45
      npm/ng-packs/packages/theme-shared/src/lib/components/errors/error.component.ts
  51. 43
      npm/ng-packs/packages/theme-shared/src/lib/components/loader-bar/loader-bar.component.ts
  52. 48
      npm/ng-packs/packages/theme-shared/src/lib/components/modal/modal.component.ts
  53. 11
      npm/ng-packs/packages/theme-shared/src/lib/components/profile/profile.component.ts
  54. 7
      npm/ng-packs/packages/theme-shared/src/lib/components/table-empty-message/table-empty-message.component.ts
  55. 3
      npm/ng-packs/packages/theme-shared/src/lib/components/toast/toast.component.ts
  56. 2
      npm/ng-packs/packages/theme-shared/src/lib/contants/scripts.ts
  57. 55
      npm/ng-packs/packages/theme-shared/src/lib/handlers/error.handler.ts
  58. 8
      npm/ng-packs/packages/theme-shared/src/lib/services/confirmation.service.ts
  59. 54
      npm/ng-packs/packages/theme-shared/src/lib/tests/button.component.spec.ts
  60. 109
      npm/ng-packs/packages/theme-shared/src/lib/tests/error.handler.spec.ts
  61. 20
      npm/ng-packs/packages/theme-shared/src/lib/theme-shared.module.ts
  62. 14
      npm/ng-packs/packages/theme-shared/tslint.json
  63. 105
      npm/ng-packs/tslint.json

3
npm/ng-packs/.prettierrc

@ -1,5 +1,4 @@
{
"printWidth": 120,
"singleQuote": true,
"trailingComma": "all"
"singleQuote": true
}

45
npm/ng-packs/angular.json

@ -27,7 +27,10 @@
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": ["packages/core/tsconfig.lib.json", "packages/core/tsconfig.spec.json"],
"tsConfig": [
"packages/core/tsconfig.lib.json",
"packages/core/tsconfig.spec.json"
],
"exclude": ["**/node_modules/**"]
}
}
@ -58,7 +61,10 @@
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": ["packages/theme-shared/tsconfig.lib.json", "packages/theme-shared/tsconfig.spec.json"],
"tsConfig": [
"packages/theme-shared/tsconfig.lib.json",
"packages/theme-shared/tsconfig.spec.json"
],
"exclude": ["**/node_modules/**"]
}
}
@ -89,7 +95,10 @@
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": ["packages/theme-basic/tsconfig.lib.json", "packages/theme-basic/tsconfig.spec.json"],
"tsConfig": [
"packages/theme-basic/tsconfig.lib.json",
"packages/theme-basic/tsconfig.spec.json"
],
"exclude": ["**/node_modules/**"]
}
}
@ -119,7 +128,10 @@
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": ["packages/account/tsconfig.lib.json", "packages/account/tsconfig.spec.json"],
"tsConfig": [
"packages/account/tsconfig.lib.json",
"packages/account/tsconfig.spec.json"
],
"exclude": ["**/node_modules/**"]
}
}
@ -182,7 +194,10 @@
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": ["packages/identity/tsconfig.lib.json", "packages/identity/tsconfig.spec.json"],
"tsConfig": [
"packages/identity/tsconfig.lib.json",
"packages/identity/tsconfig.spec.json"
],
"exclude": ["**/node_modules/**"]
}
}
@ -282,9 +297,7 @@
"packages/setting-management/tsconfig.lib.json",
"packages/setting-management/tsconfig.spec.json"
],
"exclude": [
"**/node_modules/**"
]
"exclude": ["**/node_modules/**"]
}
}
}
@ -317,9 +330,7 @@
"packages/setting-management-config/tsconfig.lib.json",
"packages/setting-management-config/tsconfig.spec.json"
],
"exclude": [
"**/node_modules/**"
]
"exclude": ["**/node_modules/**"]
}
}
}
@ -352,9 +363,7 @@
"packages/identity-config/tsconfig.lib.json",
"packages/identity-config/tsconfig.spec.json"
],
"exclude": [
"**/node_modules/**"
]
"exclude": ["**/node_modules/**"]
}
}
}
@ -387,9 +396,7 @@
"packages/account-config/tsconfig.lib.json",
"packages/account-config/tsconfig.spec.json"
],
"exclude": [
"**/node_modules/**"
]
"exclude": ["**/node_modules/**"]
}
}
}
@ -422,9 +429,7 @@
"packages/tenant-management-config/tsconfig.lib.json",
"packages/tenant-management-config/tsconfig.spec.json"
],
"exclude": [
"**/node_modules/**"
]
"exclude": ["**/node_modules/**"]
}
}
}

70
npm/ng-packs/package.json

@ -2,27 +2,29 @@
"scripts": {
"ng": "ng",
"symlink": "symlink",
"abpng": "abpng"
"abpng": "abpng",
"commit": "git-cz",
"lint": "ng lint --fix"
},
"devDependencies": {
"@abp/ng.core": "^0.9.0",
"@abp/ng.feature-management": "^0.9.0",
"@abp/ng.permission-management": "^0.9.0",
"@abp/ng.theme.shared": "^0.9.0",
"@angular-devkit/build-angular": "~0.803.5",
"@angular-devkit/build-ng-packagr": "~0.803.5",
"@angular/animations": "~8.2.7",
"@angular/cdk": "^8.0.1",
"@angular/cli": "~8.3.5",
"@angular/common": "~8.1.2",
"@angular/compiler": "~8.2.7",
"@angular/compiler-cli": "~8.2.7",
"@angular/core": "~8.1.2",
"@angular/forms": "~8.1.2",
"@angular/language-service": "~8.2.7",
"@angular/platform-browser": "~8.2.7",
"@angular/platform-browser-dynamic": "~8.2.7",
"@angular/router": "~8.1.2",
"@angular-devkit/build-angular": "~0.803.6",
"@angular-devkit/build-ng-packagr": "~0.803.6",
"@angular/animations": "~8.2.8",
"@angular/cdk": "^8.2.2",
"@angular/cli": "~8.3.6",
"@angular/common": "~8.2.8",
"@angular/compiler-cli": "~8.2.8",
"@angular/compiler": "~8.2.8",
"@angular/core": "~8.2.8",
"@angular/forms": "~8.2.8",
"@angular/language-service": "~8.2.8",
"@angular/platform-browser-dynamic": "~8.2.8",
"@angular/platform-browser": "~8.2.8",
"@angular/router": "~8.2.8",
"@ng-bootstrap/ng-bootstrap": "^5.1.0",
"@ngneat/spectator": "^4.3.2",
"@ngx-validate/core": "^0.0.5",
@ -35,32 +37,58 @@
"angular-oauth2-oidc": "^8.0.1",
"bootstrap": "^4.3.1",
"chart.js": "^2.8.0",
"codelyzer": "^5.0.0",
"codelyzer": "^5.1.2",
"cz-conventional-changelog": "3.0.2",
"font-awesome": "^4.7.0",
"husky": "^3.0.7",
"jasmine-core": "~3.4.0",
"jasmine-spec-reporter": "~4.2.1",
"just-clone": "3.1.0",
"just-compare": "^1.3.0",
"karma": "~4.1.0",
"karma-chrome-launcher": "~2.2.0",
"karma-coverage-istanbul-reporter": "~2.0.1",
"karma-jasmine": "~2.0.1",
"karma-jasmine-html-reporter": "^1.4.0",
"karma-jasmine": "~2.0.1",
"karma": "~4.1.0",
"lint-staged": "^9.4.0",
"ng-packagr": "^5.5.0",
"ngx-perfect-scrollbar": "^8.0.0",
"primeicons": "^1.0.0",
"ngxs-reset-plugin": "^1.2.0",
"prettier": "^1.18.2",
"primeicons": "^2.0.0",
"primeng": "^8.0.0",
"protractor": "~5.4.0",
"rxjs": "~6.4.0",
"snq": "^1.0.3",
"symlink-manager": "^1.3.0",
"ts-node": "~7.0.0",
"tsickle": "^0.35.0",
"tslint": "~5.15.0",
"tsickle": "^0.37.0",
"tslint": "~5.20.0",
"typescript": "~3.5.3",
"zone.js": "~0.9.1"
},
"dependencies": {
"tslib": "^1.10.0"
},
"config": {
"commitizen": {
"path": "cz-conventional-changelog"
}
},
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"lint-staged": {
"*.{ts,js,html,json,md,yaml,yml}": [
"prettier --write",
"git add"
],
"*.ts": [
"prettier --write",
"tslint --fix",
"git add"
]
}
}

33
npm/ng-packs/packages/account/src/lib/components/tenant-box/tenant-box.component.ts

@ -9,10 +9,14 @@ import { AccountService } from '../../services/account.service';
@Component({
selector: 'abp-tenant-box',
templateUrl: './tenant-box.component.html',
templateUrl: './tenant-box.component.html'
})
export class TenantBoxComponent implements OnInit {
constructor(private store: Store, private toasterService: ToasterService, private accountService: AccountService) {}
constructor(
private store: Store,
private toasterService: ToasterService,
private accountService: AccountService
) {}
tenant = {} as ABP.BasicItem;
@ -21,7 +25,9 @@ export class TenantBoxComponent implements OnInit {
isModalVisible: boolean;
ngOnInit() {
this.tenant = this.store.selectSnapshot(SessionState.getTenant) || ({} as ABP.BasicItem);
this.tenant =
this.store.selectSnapshot(SessionState.getTenant) ||
({} as ABP.BasicItem);
this.tenantName = this.tenant.name || '';
}
@ -37,24 +43,31 @@ export class TenantBoxComponent implements OnInit {
take(1),
catchError(err => {
this.toasterService.error(
snq(() => err.error.error_description, 'AbpUi::DefaultErrorMessage'),
'AbpUi::Error',
snq(
() => err.error.error_description,
'AbpUi::DefaultErrorMessage'
),
'AbpUi::Error'
);
return throwError(err);
}),
})
)
.subscribe(({ success, tenantId }) => {
if (success) {
this.tenant = {
id: tenantId,
name: this.tenant.name,
name: this.tenant.name
};
this.tenantName = this.tenant.name;
this.isModalVisible = false;
} else {
this.toasterService.error(`AbpUiMultiTenancy::GivenTenantIsNotAvailable`, 'AbpUi::Error', {
messageLocalizationParams: [this.tenant.name],
});
this.toasterService.error(
'AbpUiMultiTenancy::GivenTenantIsNotAvailable',
'AbpUi::Error',
{
messageLocalizationParams: [this.tenant.name]
}
);
this.tenant = {} as ABP.BasicItem;
}
this.store.dispatch(new SetTenant(success ? this.tenant : null));

8
npm/ng-packs/packages/account/src/lib/services/account.service.ts

@ -4,7 +4,7 @@ import { RestService, Rest } from '@abp/ng.core';
import { RegisterResponse, RegisterRequest, TenantIdResponse } from '../models';
@Injectable({
providedIn: 'root',
providedIn: 'root'
})
export class AccountService {
constructor(private rest: RestService) {}
@ -12,7 +12,7 @@ export class AccountService {
findTenant(tenantName: string): Observable<TenantIdResponse> {
const request: Rest.Request<null> = {
method: 'GET',
url: `/api/abp/multi-tenancy/tenants/by-name/${tenantName}`,
url: `/api/abp/multi-tenancy/tenants/by-name/${tenantName}`
};
return this.rest.request<null, TenantIdResponse>(request);
@ -21,8 +21,8 @@ export class AccountService {
register(body: RegisterRequest): Observable<RegisterResponse> {
const request: Rest.Request<RegisterRequest> = {
method: 'POST',
url: `/api/account/register`,
body,
url: '/api/account/register',
body
};
return this.rest.request<RegisterRequest, RegisterResponse>(request, { skipHandleError: true });

8
npm/ng-packs/packages/core/src/lib/abstracts/ng-model.component.ts

@ -1,13 +1,11 @@
import { ControlValueAccessor } from '@angular/forms';
import { ChangeDetectorRef, Component, Injector, Input, Type } from '@angular/core';
@Component({ template: '' })
@Component({ selector: 'abp-abstract-ng-model', template: '' })
export class AbstractNgModelComponent<T = any> implements ControlValueAccessor {
@Input()
disabled: boolean;
@Input() disabled: boolean;
@Input()
set value(value: T) {
@Input() set value(value: T) {
this._value = value;
this.notifyValueChange();
}

8
npm/ng-packs/packages/core/src/lib/components/dynamic-layout.component.ts

@ -13,21 +13,19 @@ import { takeUntilDestroy } from '../utils/rxjs-utils';
selector: 'abp-dynamic-layout',
template: `
<ng-container *ngTemplateOutlet="layout ? componentOutlet : routerOutlet"></ng-container>
<ng-template #routerOutlet><router-outlet></router-outlet></ng-template>
<ng-template #componentOutlet><ng-container *ngComponentOutlet="layout"></ng-container></ng-template>
`,
`
})
export class DynamicLayoutComponent implements OnDestroy {
@Select(ConfigState.getOne('requirements'))
requirements$: Observable<Config.Requirements>;
@Select(ConfigState.getOne('requirements')) requirements$: Observable<Config.Requirements>;
layout: Type<any>;
constructor(private router: Router, private route: ActivatedRoute, private store: Store) {
const {
requirements: { layouts },
routes,
routes
} = this.store.selectSnapshot(ConfigState.getAll);
if ((this.route.snapshot.data || {}).layout) {

5
npm/ng-packs/packages/core/src/lib/directives/autofocus.directive.ts

@ -1,11 +1,12 @@
import { Directive, ElementRef, Input, AfterViewInit } from '@angular/core';
@Directive({
selector: '[autofocus]',
// tslint:disable-next-line: directive-selector
selector: '[autofocus]'
})
export class AutofocusDirective implements AfterViewInit {
@Input('autofocus')
delay: number = 0;
delay = 0;
constructor(private elRef: ElementRef) {}

9
npm/ng-packs/packages/core/src/lib/directives/debounce.directive.ts

@ -4,12 +4,13 @@ import { debounceTime } from 'rxjs/operators';
import { takeUntilDestroy } from '@ngx-validate/core';
@Directive({
selector: '[input.debounce]',
// tslint:disable-next-line: directive-selector
selector: '[input.debounce]'
})
export class InputEventDebounceDirective implements OnInit {
@Input() debounce: number = 300;
@Input() debounce = 300;
@Output('input.debounce') debounceEvent = new EventEmitter<Event>();
@Output('input.debounce') readonly debounceEvent = new EventEmitter<Event>();
constructor(private renderer: Renderer2, private el: ElementRef) {}
@ -17,7 +18,7 @@ export class InputEventDebounceDirective implements OnInit {
fromEvent(this.el.nativeElement, 'input')
.pipe(
debounceTime(this.debounce),
takeUntilDestroy(this),
takeUntilDestroy(this)
)
.subscribe((event: Event) => {
this.debounceEvent.emit(event);

11
npm/ng-packs/packages/core/src/lib/directives/for.directive.ts

@ -9,7 +9,7 @@ import {
OnChanges,
TemplateRef,
TrackByFunction,
ViewContainerRef,
ViewContainerRef
} from '@angular/core';
import compare from 'just-compare';
import clone from 'just-clone';
@ -25,7 +25,7 @@ class RecordView {
}
@Directive({
selector: '[abpFor]',
selector: '[abpFor]'
})
export class ForDirective implements OnChanges {
@Input('abpForOf')
@ -67,7 +67,7 @@ export class ForDirective implements OnChanges {
constructor(
private tempRef: TemplateRef<AbpForContext>,
private vcRef: ViewContainerRef,
private differs: IterableDiffers,
private differs: IterableDiffers
) {}
private iterateOverAppliedOperations(changes: IterableChanges<any>) {
@ -78,7 +78,7 @@ export class ForDirective implements OnChanges {
const view = this.vcRef.createEmbeddedView(
this.tempRef,
new AbpForContext(null, -1, -1, this.items),
currentIndex,
currentIndex
);
rw.push(new RecordView(record, view));
@ -113,8 +113,11 @@ export class ForDirective implements OnChanges {
private projectItems(items: any[]): void {
if (!items.length && this.emptyRef) {
this.vcRef.clear();
// tslint:disable-next-line: no-unused-expression
this.vcRef.createEmbeddedView(this.emptyRef).rootNodes;
this.isShowEmptyRef = true;
this.differ = null;
return;
}

16
npm/ng-packs/packages/core/src/lib/directives/form-submit.directive.ts

@ -7,7 +7,7 @@ import {
OnDestroy,
OnInit,
Output,
Self,
Self
} from '@angular/core';
import { FormControl, FormGroup, FormGroupDirective } from '@angular/forms';
import { fromEvent } from 'rxjs';
@ -17,21 +17,21 @@ import { takeUntilDestroy } from '../utils';
type Controls = { [key: string]: FormControl } | FormGroup[];
@Directive({
selector: 'form[ngSubmit][formGroup]',
// tslint:disable-next-line: directive-selector
selector: 'form[ngSubmit][formGroup]'
})
export class FormSubmitDirective implements OnInit, OnDestroy {
@Input()
notValidateOnSubmit: string | boolean;
@Output()
ngSubmit = new EventEmitter();
@Output() readonly ngSubmit = new EventEmitter();
executedNgSubmit: boolean = false;
executedNgSubmit = false;
constructor(
@Self() private formGroupDirective: FormGroupDirective,
private host: ElementRef<HTMLFormElement>,
private cdRef: ChangeDetectorRef,
private cdRef: ChangeDetectorRef
) {}
ngOnInit() {
@ -44,7 +44,7 @@ export class FormSubmitDirective implements OnInit, OnDestroy {
.pipe(
debounceTime(200),
filter((key: KeyboardEvent) => key && key.key === 'Enter'),
takeUntilDestroy(this),
takeUntilDestroy(this)
)
.subscribe(() => {
if (!this.executedNgSubmit) {
@ -57,7 +57,7 @@ export class FormSubmitDirective implements OnInit, OnDestroy {
fromEvent(this.host.nativeElement, 'submit')
.pipe(
takeUntilDestroy(this),
filter(() => !this.notValidateOnSubmit && typeof this.notValidateOnSubmit !== 'string'),
filter(() => !this.notValidateOnSubmit && typeof this.notValidateOnSubmit !== 'string')
)
.subscribe(() => {
if (!this.executedNgSubmit) {

5
npm/ng-packs/packages/core/src/lib/directives/stop-propagation.directive.ts

@ -3,10 +3,11 @@ import { fromEvent } from 'rxjs';
import { takeUntilDestroy } from '@ngx-validate/core';
@Directive({
selector: '[click.stop]',
// tslint:disable-next-line: directive-selector
selector: '[click.stop]'
})
export class ClickEventStopPropagationDirective implements OnInit {
@Output('click.stop') stopPropEvent = new EventEmitter<MouseEvent>();
@Output('click.stop') readonly stopPropEvent = new EventEmitter<MouseEvent>();
constructor(private renderer: Renderer2, private el: ElementRef) {}

10
npm/ng-packs/packages/core/src/lib/directives/visibility.directive.ts

@ -3,14 +3,14 @@ import { Subject } from 'rxjs';
import snq from 'snq';
@Directive({
selector: '[abpVisibility]',
selector: '[abpVisibility]'
})
export class VisibilityDirective implements AfterViewInit {
@Input('abpVisibility')
focusedElement: HTMLElement;
@Input()
mutationObserverEnabled: boolean = true;
mutationObserverEnabled = true;
completed$ = new Subject<boolean>();
@ -25,7 +25,7 @@ export class VisibilityDirective implements AfterViewInit {
const htmlNodes = snq(
() => Array.from(mutation.target.childNodes).filter(node => node instanceof HTMLElement),
[],
[]
);
if (!htmlNodes.length) {
@ -40,13 +40,13 @@ export class VisibilityDirective implements AfterViewInit {
});
observer.observe(this.focusedElement, {
childList: true,
childList: true
});
} else {
setTimeout(() => {
const htmlNodes = snq(
() => Array.from(this.focusedElement.childNodes).filter(node => node instanceof HTMLElement),
[],
[]
);
if (!htmlNodes.length) this.removeFromDOM();

2
npm/ng-packs/packages/core/src/lib/pipes/index.ts

@ -1,2 +1,2 @@
export * from './localization.pipe';
export * from "./sort.pipe";
export * from './sort.pipe';

11
npm/ng-packs/packages/core/src/lib/pipes/localization.pipe.ts

@ -7,10 +7,11 @@ import { Subject } from 'rxjs';
@Pipe({
name: 'abpLocalization',
pure: false, // required to update the value
// tslint:disable-next-line: no-pipe-impure
pure: false // required to update the value
})
export class LocalizationPipe implements PipeTransform, OnDestroy {
initialValue: string = '';
initialValue = '';
value: string;
@ -27,13 +28,13 @@ export class LocalizationPipe implements PipeTransform, OnDestroy {
.select(
ConfigState.getCopy(
value,
...interpolateParams.reduce((acc, val) => (Array.isArray(val) ? [...acc, ...val] : [...acc, val]), []),
),
...interpolateParams.reduce((acc, val) => (Array.isArray(val) ? [...acc, ...val] : [...acc, val]), [])
)
)
.pipe(
takeUntil(this.destroy$),
takeUntilDestroy(this),
distinctUntilChanged(),
distinctUntilChanged()
)
.subscribe(copy => (this.value = copy));
}

17
npm/ng-packs/packages/core/src/lib/pipes/sort.pipe.ts

@ -1,13 +1,14 @@
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({
name: 'abpSort',
pure: false
name: 'abpSort',
// tslint:disable-next-line: no-pipe-impure
pure: false
})
export class SortPipe implements PipeTransform {
transform(value: any[], sortOrder: string): any {
sortOrder = sortOrder.toLowerCase();
if(sortOrder === "desc") return value.reverse();
else return value;
}
}
transform(value: any[], sortOrder: string): any {
sortOrder = sortOrder.toLowerCase();
if (sortOrder === 'desc') return value.reverse();
else return value;
}
}

20
npm/ng-packs/packages/core/src/lib/plugins/config/config.plugin.ts

@ -12,7 +12,7 @@ export let ABP_ROUTES = [] as ABP.FullRoute[];
@Injectable()
export class ConfigPlugin implements NgxsPlugin {
private initialized: boolean = false;
private initialized = false;
constructor(@Inject(NGXS_CONFIG_PLUGIN_OPTIONS) private options: ABP.Root, private router: Router) {}
@ -22,14 +22,17 @@ export class ConfigPlugin implements NgxsPlugin {
// const layouts = snq(() => this.options.requirements.layouts.filter(layout => layout instanceof Type), []);
if (isInitAction && !this.initialized) {
let { routes, wrappers } = transformRoutes(this.router.config);
const transformedRoutes = transformRoutes(this.router.config);
let { routes } = transformedRoutes;
const { wrappers } = transformedRoutes;
routes = organizeRoutes(routes, wrappers);
const flattedRoutes = flatRoutes(clone(routes));
state = setValue(state, 'ConfigState', {
...(state.ConfigState && { ...state.ConfigState }),
...this.options,
routes,
flattedRoutes,
flattedRoutes
});
this.initialized = true;
@ -49,6 +52,7 @@ function transformRoutes(routes: Routes = [], wrappers: ABP.FullRoute[] = []): a
return snq(() => route.data.routes.routes.find(r => r.path === route.path), false);
})
.reduce((acc, val) => [...acc, ...val.data.routes.routes], []);
// tslint:disable-next-line: deprecation
ABP_ROUTES = [...ABP_ROUTES, ...abpRoutes];
wrappers = ABP_ROUTES.filter(ar => ar.wrapper);
@ -69,7 +73,7 @@ function transformRoutes(routes: Routes = [], wrappers: ABP.FullRoute[] = []): a
...route.data.routes,
path: route.path,
name: snq(() => route.data.routes.name, route.path),
children: route.data.routes.children || [],
children: route.data.routes.children || []
} as ABP.FullRoute);
}
});
@ -86,8 +90,8 @@ function setUrls(routes: ABP.FullRoute[], parentUrl?: string): ABP.FullRoute[] {
url: `${parentUrl}/${route.path}`,
...(route.children &&
route.children.length && {
children: setUrls(route.children, `${parentUrl}/${route.path}`),
}),
children: setUrls(route.children, `${parentUrl}/${route.path}`)
})
}));
}
@ -96,8 +100,8 @@ function setUrls(routes: ABP.FullRoute[], parentUrl?: string): ABP.FullRoute[] {
url: `/${route.path}`,
...(route.children &&
route.children.length && {
children: setUrls(route.children, `/${route.path}`),
}),
children: setUrls(route.children, `/${route.path}`)
})
}));
}

46
npm/ng-packs/packages/core/src/lib/states/config.state.ts

@ -11,7 +11,7 @@ import { setChildRoute, sortRoutes, organizeRoutes } from '../utils/route-utils'
@State<Config.State>({
name: 'ConfigState',
defaults: {} as Config.State,
defaults: {} as Config.State
})
export class ConfigState {
@Selector()
@ -27,9 +27,9 @@ export class ConfigState {
static getOne(key: string) {
const selector = createSelector(
[ConfigState],
function(state: Config.State) {
(state: Config.State) => {
return state[key];
},
}
);
return selector;
@ -46,7 +46,7 @@ export class ConfigState {
const selector = createSelector(
[ConfigState],
function(state: Config.State) {
(state: Config.State) => {
return (keys as string[]).reduce((acc, val) => {
if (acc) {
return acc[val];
@ -54,7 +54,7 @@ export class ConfigState {
return undefined;
}, state);
},
}
);
return selector;
@ -63,7 +63,7 @@ export class ConfigState {
static getRoute(path?: string, name?: string) {
const selector = createSelector(
[ConfigState],
function(state: Config.State) {
(state: Config.State) => {
const { flattedRoutes } = state;
return (flattedRoutes as ABP.FullRoute[]).find(route => {
if (path && route.path === path) {
@ -72,7 +72,7 @@ export class ConfigState {
return route;
}
});
},
}
);
return selector;
@ -81,9 +81,9 @@ export class ConfigState {
static getApiUrl(key?: string) {
const selector = createSelector(
[ConfigState],
function(state: Config.State): string {
(state: Config.State): string => {
return state.environment.apis[key || 'default'].url;
},
}
);
return selector;
@ -92,9 +92,9 @@ export class ConfigState {
static getSetting(key: string) {
const selector = createSelector(
[ConfigState],
function(state: Config.State) {
(state: Config.State) => {
return snq(() => state.setting.values[key]);
},
}
);
return selector;
@ -103,10 +103,10 @@ export class ConfigState {
static getGrantedPolicy(key: string) {
const selector = createSelector(
[ConfigState],
function(state: Config.State): boolean {
(state: Config.State): boolean => {
if (!key) return true;
return snq(() => state.auth.grantedPolicies[key], false);
},
}
);
return selector;
@ -118,20 +118,20 @@ export class ConfigState {
const keys = key.split('::') as string[];
const selector = createSelector(
[ConfigState],
function(state: Config.State) {
(state: Config.State) => {
if (!state.localization) return key;
const { defaultResourceName } = state.environment.localization;
if (keys[0] === '') {
if (!defaultResourceName) {
throw new Error(
`Please check your environment. May you forget set defaultResourceName?
`Please check your environment. May you forget set defaultResourceName?
Here is the example:
{ production: false,
localization: {
defaultResourceName: 'MyProjectName'
}
}`,
}`
);
}
@ -154,7 +154,7 @@ export class ConfigState {
}
return copy || key;
},
}
);
return selector;
@ -167,8 +167,8 @@ export class ConfigState {
return this.appConfigurationService.getConfiguration().pipe(
tap(configuration =>
patchState({
...configuration,
}),
...configuration
})
),
switchMap(configuration => {
let defaultLang: string = configuration.setting.values['Abp.Localization.DefaultLanguage'];
@ -178,7 +178,7 @@ export class ConfigState {
}
return this.store.selectSnapshot(SessionState.getLanguage) ? of(null) : dispatch(new SetLanguage(defaultLang));
}),
})
);
}
@ -191,7 +191,7 @@ export class ConfigState {
routes = patchRouteDeep(routes, name, newValue);
return patchState({
routes,
routes
});
}
}
@ -200,7 +200,7 @@ function patchRouteDeep(
routes: ABP.FullRoute[],
name: string,
newValue: Partial<ABP.FullRoute>,
parentUrl: string = null,
parentUrl: string = null
): ABP.FullRoute[] {
routes = routes.map(route => {
if (route.name === name) {
@ -211,7 +211,7 @@ function patchRouteDeep(
if (newValue.children && newValue.children.length) {
newValue.children = newValue.children.map(child => ({
...child,
url: `${parentUrl}/${route.path}/${child.path}`,
url: `${parentUrl}/${route.path}/${child.path}`
}));
}

154
npm/ng-packs/packages/core/src/lib/tests/dynamic-layout.component.spec.ts

@ -0,0 +1,154 @@
import { ConfigState, CoreModule, DynamicLayoutComponent, eLayoutType, ABP, RouterOutletComponent } from '@abp/ng.core';
import { Location } from '@angular/common';
import { Component } from '@angular/core';
import { createRoutingFactory, SpectatorRouting, SpyObject } from '@ngneat/spectator';
import { NgxsModule, Store } from '@ngxs/store';
import { LAYOUTS, ThemeBasicModule } from '../../../../theme-basic/src/public-api';
import { OAuthService } from 'angular-oauth2-oidc';
import { NgxsResetPluginModule, StateOverwrite } from 'ngxs-reset-plugin';
import { ThemeSharedModule } from '../../../../theme-shared/src/public-api';
import { ActivatedRoute } from '@angular/router';
@Component({
selector: 'abp-dummy',
template: '{{route.snapshot.data?.name}} works!'
})
class DummyComponent {
constructor(public route: ActivatedRoute) {}
}
describe('DynamicLayoutComponent', () => {
const createComponent = createRoutingFactory({
component: RouterOutletComponent,
declareComponent: false,
imports: [
CoreModule,
NgxsModule.forRoot([ConfigState]),
NgxsResetPluginModule.forRoot(),
ThemeSharedModule.forRoot(),
ThemeBasicModule
],
declarations: [DummyComponent],
stubsEnabled: false,
providers: [{ provide: OAuthService, useValue: { getAccessToken: () => true } }],
routes: [
{ path: '', component: RouterOutletComponent },
{
path: 'parentWithLayout',
component: DynamicLayoutComponent,
children: [
{
path: 'childWithoutLayout',
component: DummyComponent,
data: { name: 'childWithoutLayout' }
},
{
path: 'childWithLayout',
component: DummyComponent,
data: { name: 'childWithLayout' }
}
]
},
{
path: 'withData',
component: DynamicLayoutComponent,
children: [
{
path: '',
component: DummyComponent,
data: { name: 'withData' }
}
],
data: { layout: eLayoutType.empty }
},
{
path: 'withoutLayout',
component: DynamicLayoutComponent,
children: [
{
path: '',
component: DummyComponent,
data: { name: 'withoutLayout' }
}
],
data: { layout: null }
}
]
});
let spectator: SpectatorRouting<RouterOutletComponent>;
let store: SpyObject<Store>;
beforeEach(async () => {
spectator = createComponent();
store = spectator.get(Store);
store.dispatch(
new StateOverwrite([
ConfigState,
{
requirements: { layouts: LAYOUTS },
routes: [
{
path: '',
wrapper: true,
children: [
{
path: 'parentWithLayout',
layout: eLayoutType.application,
children: [{ path: 'childWithoutLayout' }, { path: 'childWithLayout', layout: eLayoutType.account }]
}
]
},
{ path: 'withData', layout: eLayoutType.application },
,
] as ABP.FullRoute[],
environment: { application: {} }
}
])
);
});
it('should handle application layout from parent abp route and display it', async () => {
spectator.router.navigateByUrl('/parentWithLayout/childWithoutLayout');
await spectator.fixture.whenStable();
spectator.detectComponentChanges();
expect(spectator.query('abp-dynamic-layout')).toBeTruthy();
expect(spectator.query('abp-layout-application')).toBeTruthy();
});
it('should handle account layout from own property and display it', async () => {
spectator.router.navigateByUrl('/parentWithLayout/childWithLayout');
await spectator.fixture.whenStable();
spectator.detectComponentChanges();
expect(spectator.query('abp-layout-account')).toBeTruthy();
});
it('should handle empty layout from route data and display it', async () => {
spectator.router.navigateByUrl('/withData');
await spectator.fixture.whenStable();
spectator.detectComponentChanges();
expect(spectator.query('abp-layout-empty')).toBeTruthy();
});
it('should display empty layout when layout is null', async () => {
spectator.router.navigateByUrl('/withoutLayout');
await spectator.fixture.whenStable();
spectator.detectComponentChanges();
expect(spectator.query('abp-layout-empty')).toBeTruthy();
});
it('should not display any layout when layouts are empty', async () => {
store.dispatch(
new StateOverwrite([ConfigState, { ...store.selectSnapshot(ConfigState), requirements: { layouts: [] } }])
);
spectator.detectChanges();
spectator.router.navigateByUrl('/withoutLayout');
await spectator.fixture.whenStable();
spectator.detectComponentChanges();
expect(spectator.query('abp-layout-empty')).toBeFalsy();
expect(spectator.query('abp-dynamic-layout').children[0].tagName).toEqual('ROUTER-OUTLET');
});
});

15
npm/ng-packs/packages/core/src/lib/tests/router-outlet.component.spec.ts

@ -0,0 +1,15 @@
import { Spectator, createComponentFactory, createHostFactory } from '@ngneat/spectator';
import { RouterOutletComponent } from '@abp/ng.core';
import { RouterTestingModule } from '@angular/router/testing';
describe('RouterOutletComponent', () => {
let spectator: Spectator<RouterOutletComponent>;
const createHost = createHostFactory({ component: RouterOutletComponent, imports: [RouterTestingModule] });
it('should have a router-outlet element', () => {
spectator = createHost('<abp-router-outlet></abp-router-outlet>');
console.log((spectator.debugElement.nativeElement as HTMLElement).children);
expect((spectator.debugElement.nativeElement as HTMLElement).children.length).toBe(1);
expect((spectator.debugElement.nativeElement as HTMLElement).children[0].tagName).toBe('ROUTER-OUTLET');
});
});

1
npm/ng-packs/packages/core/src/lib/utils/common-utils.ts

@ -1,4 +1,5 @@
export function noop() {
// tslint:disable-next-line: only-arrow-functions
const fn = function() {};
return fn;
}

3
npm/ng-packs/packages/core/src/lib/utils/generator-utils.ts

@ -1,5 +1,6 @@
export function uuid(a?: any): string {
return a
? (a ^ ((Math.random() * 16) >> (a / 4))).toString(16)
? // tslint:disable-next-line: no-bitwise
(a ^ ((Math.random() * 16) >> (a / 4))).toString(16)
: ('' + 1e7 + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, uuid);
}

4
npm/ng-packs/packages/core/src/lib/utils/initial-utils.ts

@ -6,7 +6,7 @@ import differentLocales from '../constants/different-locales';
import { SessionState } from '../states/session.state';
export function getInitialData(injector: Injector) {
const fn = function() {
const fn = () => {
const store: Store = injector.get(Store);
return store.dispatch(new GetAppConfiguration()).toPromise();
@ -16,7 +16,7 @@ export function getInitialData(injector: Injector) {
}
export function localeInitializer(injector: Injector) {
const fn = function() {
const fn = () => {
const store: Store = injector.get(Store);
const lang = store.selectSnapshot(SessionState.getLanguage) || 'en';

5
npm/ng-packs/packages/core/src/lib/utils/rxjs-utils.ts

@ -6,18 +6,19 @@ function isFunction(value) {
}
export const takeUntilDestroy = (componentInstance, destroyMethodName = 'ngOnDestroy') => <T>(
source: Observable<T>,
source: Observable<T>
) => {
const originalDestroy = componentInstance[destroyMethodName];
if (isFunction(originalDestroy) === false) {
throw new Error(
`${componentInstance.constructor.name} is using untilDestroyed but doesn't implement ${destroyMethodName}`,
`${componentInstance.constructor.name} is using untilDestroyed but doesn't implement ${destroyMethodName}`
);
}
if (!componentInstance['__takeUntilDestroy']) {
componentInstance['__takeUntilDestroy'] = new Subject();
componentInstance[destroyMethodName] = function() {
// tslint:disable-next-line: no-unused-expression
isFunction(originalDestroy) && originalDestroy.apply(this, arguments);
componentInstance['__takeUntilDestroy'].next(true);
componentInstance['__takeUntilDestroy'].complete();

20
npm/ng-packs/packages/feature-management/src/lib/components/feature-management/feature-management.component.ts

@ -9,7 +9,7 @@ import { pluck, tap } from 'rxjs/operators';
@Component({
selector: 'abp-feature-management',
templateUrl: './feature-management.component.html',
templateUrl: './feature-management.component.html'
})
export class FeatureManagementComponent {
@Input()
@ -32,13 +32,12 @@ export class FeatureManagementComponent {
if (value) this.openModal();
}
@Output()
visibleChange = new EventEmitter<boolean>();
@Output() readonly visibleChange = new EventEmitter<boolean>();
@Select(FeatureManagementState.getFeatures)
features$: Observable<FeatureManagement.Feature[]>;
modalBusy: boolean = false;
modalBusy = false;
form: FormGroup;
@ -54,7 +53,12 @@ export class FeatureManagementComponent {
getFeatures() {
this.store
.dispatch(new GetFeatures({ providerKey: this.providerKey, providerName: this.providerName }))
.dispatch(
new GetFeatures({
providerKey: this.providerKey,
providerName: this.providerName
})
)
.pipe(pluck('FeatureManagementState', 'features'))
.subscribe(features => {
this.buildForm(features);
@ -78,7 +82,7 @@ export class FeatureManagementComponent {
features = features.map((feature, i) => ({
name: feature.name,
value: !this.form.value[i] || this.form.value[i] === 'false' ? null : this.form.value[i],
value: !this.form.value[i] || this.form.value[i] === 'false' ? null : this.form.value[i]
}));
this.store
@ -86,8 +90,8 @@ export class FeatureManagementComponent {
new UpdateFeatures({
providerKey: this.providerKey,
providerName: this.providerName,
features,
}),
features
})
)
.subscribe(() => {
this.modalBusy = false;

2
npm/ng-packs/packages/feature-management/src/lib/states/index.ts

@ -1 +1 @@
export * from "./feature-management.state";
export * from './feature-management.state';

20
npm/ng-packs/packages/identity/src/lib/components/roles/roles.component.ts

@ -11,7 +11,7 @@ import { IdentityState } from '../../states/identity.state';
@Component({
selector: 'abp-roles',
templateUrl: './roles.component.html',
templateUrl: './roles.component.html'
})
export class RolesComponent {
@Select(IdentityState.getRoles)
@ -26,19 +26,19 @@ export class RolesComponent {
isModalVisible: boolean;
visiblePermissions: boolean = false;
visiblePermissions = false;
providerKey: string;
pageQuery: ABP.PageQueryParams = {
sorting: 'name',
sorting: 'name'
};
loading: boolean = false;
loading = false;
modalBusy: boolean = false;
modalBusy = false;
sortOrder: string = 'asc';
sortOrder = 'asc';
@ViewChild('modalContent', { static: false })
modalContent: TemplateRef<any>;
@ -54,10 +54,10 @@ export class RolesComponent {
this.form = this.fb.group({
name: new FormControl({ value: this.selected.name || '', disabled: this.selected.isStatic }, [
Validators.required,
Validators.maxLength(256),
Validators.maxLength(256)
]),
isDefault: [this.selected.isDefault || false],
isPublic: [this.selected.isPublic || false],
isPublic: [this.selected.isPublic || false]
});
}
@ -89,7 +89,7 @@ export class RolesComponent {
.dispatch(
this.selected.id
? new UpdateRole({ ...this.form.value, id: this.selected.id })
: new CreateRole(this.form.value),
: new CreateRole(this.form.value)
)
.subscribe(() => {
this.modalBusy = false;
@ -100,7 +100,7 @@ export class RolesComponent {
delete(id: string, name: string) {
this.confirmationService
.warn('AbpIdentity::RoleDeletionConfirmationMessage', 'AbpIdentity::AreYouSure', {
messageLocalizationParams: [name],
messageLocalizationParams: [name]
})
.subscribe((status: Toaster.Status) => {
if (status === Toaster.Status.confirm) {

34
npm/ng-packs/packages/identity/src/lib/components/users/users.component.ts

@ -12,13 +12,13 @@ import {
GetUserById,
GetUserRoles,
GetUsers,
UpdateUser,
UpdateUser
} from '../../actions/identity.actions';
import { Identity } from '../../models/identity';
import { IdentityState } from '../../states/identity.state';
@Component({
selector: 'abp-users',
templateUrl: './users.component.html',
templateUrl: './users.component.html'
})
export class UsersComponent {
@Select(IdentityState.getUsers)
@ -38,21 +38,21 @@ export class UsersComponent {
roles: Identity.RoleItem[];
visiblePermissions: boolean = false;
visiblePermissions = false;
providerKey: string;
pageQuery: ABP.PageQueryParams = {
sorting: 'userName',
sorting: 'userName'
};
isModalVisible: boolean;
loading: boolean = false;
loading = false;
modalBusy: boolean = false;
modalBusy = false;
sortOrder: string = 'asc';
sortOrder = 'asc';
trackByFn: TrackByFunction<AbstractControl> = (index, item) => Object.keys(item)[0] || index;
@ -80,10 +80,10 @@ export class UsersComponent {
roleNames: this.fb.array(
this.roles.map(role =>
this.fb.group({
[role.name]: [!!snq(() => this.selectedUserRoles.find(userRole => userRole.id === role.id))],
}),
),
),
[role.name]: [!!snq(() => this.selectedUserRoles.find(userRole => userRole.id === role.id))]
})
)
)
});
if (!this.selected.userName) {
this.form.addControl('password', new FormControl('', [Validators.required, Validators.maxLength(32)]));
@ -107,7 +107,7 @@ export class UsersComponent {
.pipe(
switchMap(() => this.store.dispatch(new GetUserRoles(id))),
pluck('IdentityState'),
take(1),
take(1)
)
.subscribe((state: Identity.State) => {
this.selected = state.selectedUser;
@ -123,7 +123,7 @@ export class UsersComponent {
const { roleNames } = this.form.value;
const mappedRoleNames = snq(
() => roleNames.filter(role => !!role[Object.keys(role)[0]]).map(role => Object.keys(role)[0]),
[],
[]
);
this.store
@ -132,12 +132,12 @@ export class UsersComponent {
? new UpdateUser({
...this.form.value,
id: this.selected.id,
roleNames: mappedRoleNames,
roleNames: mappedRoleNames
})
: new CreateUser({
...this.form.value,
roleNames: mappedRoleNames,
}),
roleNames: mappedRoleNames
})
)
.subscribe(() => {
this.modalBusy = false;
@ -148,7 +148,7 @@ export class UsersComponent {
delete(id: string, userName: string) {
this.confirmationService
.warn('AbpIdentity::UserDeletionConfirmationMessage', 'AbpIdentity::AreYouSure', {
messageLocalizationParams: [userName],
messageLocalizationParams: [userName]
})
.subscribe((status: Toaster.Status) => {
if (status === Toaster.Status.confirm) {

49
npm/ng-packs/packages/permission-management/src/lib/components/permission-management.component.ts

@ -7,7 +7,7 @@ import {
Output,
Renderer2,
SimpleChanges,
TrackByFunction,
TrackByFunction
} from '@angular/core';
import { Select, Store } from '@ngxs/store';
import { from, Observable } from 'rxjs';
@ -16,11 +16,13 @@ import { GetPermissions, UpdatePermissions } from '../actions/permission-managem
import { PermissionManagement } from '../models/permission-management';
import { PermissionManagementState } from '../states/permission-management.state';
type PermissionWithMargin = PermissionManagement.Permission & { margin: number };
type PermissionWithMargin = PermissionManagement.Permission & {
margin: number;
};
@Component({
selector: 'abp-permission-management',
templateUrl: './permission-management.component.html',
templateUrl: './permission-management.component.html'
})
export class PermissionManagementComponent implements OnInit, OnChanges {
@Input()
@ -47,8 +49,7 @@ export class PermissionManagementComponent implements OnInit, OnChanges {
}
}
@Output()
visibleChange = new EventEmitter<boolean>();
@Output() readonly visibleChange = new EventEmitter<boolean>();
@Select(PermissionManagementState.getPermissionGroups)
groups$: Observable<PermissionManagement.Group[]>;
@ -60,18 +61,18 @@ export class PermissionManagementComponent implements OnInit, OnChanges {
permissions: PermissionManagement.Permission[] = [];
selectThisTab: boolean = false;
selectThisTab = false;
selectAllTab: boolean = false;
selectAllTab = false;
modalBusy: boolean = false;
modalBusy = false;
trackByFn: TrackByFunction<PermissionManagement.Group> = (_, item) => item.name;
get selectedGroupPermissions$(): Observable<PermissionWithMargin[]> {
return this.groups$.pipe(
map(groups =>
this.selectedGroup ? groups.find(group => group.name === this.selectedGroup.name).permissions : [],
this.selectedGroup ? groups.find(group => group.name === this.selectedGroup.name).permissions : []
),
map<PermissionManagement.Permission[], PermissionWithMargin[]>(permissions =>
permissions.map(
@ -79,10 +80,10 @@ export class PermissionManagementComponent implements OnInit, OnChanges {
(({
...permission,
margin: findMargin(permissions, permission),
isGranted: this.permissions.find(per => per.name === permission.name).isGranted,
} as any) as PermissionWithMargin),
),
),
isGranted: this.permissions.find(per => per.name === permission.name).isGranted
} as any) as PermissionWithMargin)
)
)
);
}
@ -164,7 +165,7 @@ export class PermissionManagementComponent implements OnInit, OnChanges {
this.permissions = [
...this.permissions.slice(0, index),
{ ...this.permissions[index], isGranted: !this.selectThisTab },
...this.permissions.slice(index + 1),
...this.permissions.slice(index + 1)
];
});
});
@ -173,7 +174,10 @@ export class PermissionManagementComponent implements OnInit, OnChanges {
}
onClickSelectAll() {
this.permissions = this.permissions.map(permission => ({ ...permission, isGranted: !this.selectAllTab }));
this.permissions = this.permissions.map(permission => ({
...permission,
isGranted: !this.selectAllTab
}));
this.selectThisTab = !this.selectAllTab;
}
@ -186,12 +190,12 @@ export class PermissionManagementComponent implements OnInit, OnChanges {
submit() {
this.modalBusy = true;
const unchangedPermissions = getPermissions(
this.store.selectSnapshot(PermissionManagementState.getPermissionGroups),
this.store.selectSnapshot(PermissionManagementState.getPermissionGroups)
);
const changedPermissions: PermissionManagement.MinimumPermission[] = this.permissions
.filter(per =>
unchangedPermissions.find(unchanged => unchanged.name === per.name).isGranted === per.isGranted ? false : true,
unchangedPermissions.find(unchanged => unchanged.name === per.name).isGranted === per.isGranted ? false : true
)
.map(({ name, isGranted }) => ({ name, isGranted }));
@ -201,8 +205,8 @@ export class PermissionManagementComponent implements OnInit, OnChanges {
new UpdatePermissions({
providerKey: this.providerKey,
providerName: this.providerName,
permissions: changedPermissions,
}),
permissions: changedPermissions
})
)
.subscribe(() => {
this.modalBusy = false;
@ -220,7 +224,12 @@ export class PermissionManagementComponent implements OnInit, OnChanges {
}
this.store
.dispatch(new GetPermissions({ providerKey: this.providerKey, providerName: this.providerName }))
.dispatch(
new GetPermissions({
providerKey: this.providerKey,
providerName: this.providerName
})
)
.pipe(pluck('PermissionManagementState', 'permissionRes'))
.subscribe((permissionRes: PermissionManagement.Response) => {
this.selectedGroup = permissionRes.groups[0];

2
npm/ng-packs/packages/permission-management/src/lib/states/index.ts

@ -1 +1 @@
export * from "./permission-management.state";
export * from './permission-management.state';

40
npm/ng-packs/packages/tenant-management/src/lib/components/tenants/tenants.component.ts

@ -10,20 +10,20 @@ import {
DeleteTenant,
GetTenants,
GetTenantById,
UpdateTenant,
UpdateTenant
} from '../../actions/tenant-management.actions';
import { TenantManagementService } from '../../services/tenant-management.service';
import { TenantManagementState } from '../../states/tenant-management.state';
type SelectedModalContent = {
interface SelectedModalContent {
type: string;
title: string;
template: TemplateRef<any>;
};
}
@Component({
selector: 'abp-tenants',
templateUrl: './tenants.component.html',
templateUrl: './tenants.component.html'
})
export class TenantsComponent {
@Select(TenantManagementState.get)
@ -44,21 +44,21 @@ export class TenantsComponent {
selectedModalContent = {} as SelectedModalContent;
visibleFeatures: boolean = false;
visibleFeatures = false;
providerKey: string;
_useSharedDatabase: boolean;
pageQuery: ABP.PageQueryParams = {
sorting: 'name',
sorting: 'name'
};
loading: boolean = false;
loading = false;
modalBusy: boolean = false;
modalBusy = false;
sortOrder: string = 'asc';
sortOrder = 'asc';
get useSharedDatabase(): boolean {
return this.defaultConnectionStringForm.get('useSharedDatabase').value;
@ -78,7 +78,7 @@ export class TenantsComponent {
private confirmationService: ConfirmationService,
private tenantService: TenantManagementService,
private fb: FormBuilder,
private store: Store,
private store: Store
) {}
onSearch(value) {
@ -88,14 +88,14 @@ export class TenantsComponent {
private createTenantForm() {
this.tenantForm = this.fb.group({
name: [this.selected.name || '', [Validators.required, Validators.maxLength(256)]],
name: [this.selected.name || '', [Validators.required, Validators.maxLength(256)]]
});
}
private createDefaultConnectionStringForm() {
this.defaultConnectionStringForm = this.fb.group({
useSharedDatabase: this._useSharedDatabase,
defaultConnectionString: [this.defaultConnectionString || ''],
defaultConnectionString: [this.defaultConnectionString || '']
});
}
@ -103,7 +103,7 @@ export class TenantsComponent {
this.selectedModalContent = {
title,
template,
type,
type
};
this.isModalVisible = true;
@ -117,7 +117,7 @@ export class TenantsComponent {
switchMap(selected => {
this.selected = selected;
return this.tenantService.getDefaultConnectionString(id);
}),
})
)
.subscribe(fetchedConnectionString => {
this._useSharedDatabase = fetchedConnectionString ? false : true;
@ -158,7 +158,7 @@ export class TenantsComponent {
.deleteDefaultConnectionString(this.selected.id)
.pipe(
take(1),
finalize(() => (this.modalBusy = false)),
finalize(() => (this.modalBusy = false))
)
.subscribe(() => {
this.isModalVisible = false;
@ -168,7 +168,7 @@ export class TenantsComponent {
.updateDefaultConnectionString({ id: this.selected.id, defaultConnectionString: this.connectionString })
.pipe(
take(1),
finalize(() => (this.modalBusy = false)),
finalize(() => (this.modalBusy = false))
)
.subscribe(() => {
this.isModalVisible = false;
@ -184,9 +184,9 @@ export class TenantsComponent {
.dispatch(
this.selected.id
? new UpdateTenant({ ...this.tenantForm.value, id: this.selected.id })
: new CreateTenant(this.tenantForm.value),
: new CreateTenant(this.tenantForm.value)
)
.pipe(finalize(()=> (this.modalBusy = false)))
.pipe(finalize(() => (this.modalBusy = false)))
.subscribe(() => {
this.isModalVisible = false;
});
@ -195,7 +195,7 @@ export class TenantsComponent {
delete(id: string, name: string) {
this.confirmationService
.warn('AbpTenantManagement::TenantDeletionConfirmationMessage', 'AbpTenantManagement::AreYouSure', {
messageLocalizationParams: [name],
messageLocalizationParams: [name]
})
.subscribe((status: Toaster.Status) => {
if (status === Toaster.Status.confirm) {
@ -220,6 +220,6 @@ export class TenantsComponent {
}
changeSortOrder() {
this.sortOrder = this.sortOrder.toLowerCase() === "asc" ? "desc" : "asc";
this.sortOrder = this.sortOrder.toLowerCase() === 'asc' ? 'desc' : 'asc';
}
}

20
npm/ng-packs/packages/tenant-management/src/lib/services/tenant-management.service.ts

@ -4,7 +4,7 @@ import { RestService, Rest, ABP } from '@abp/ng.core';
import { TenantManagement } from '../models/tenant-management';
@Injectable({
providedIn: 'root',
providedIn: 'root'
})
export class TenantManagementService {
constructor(private rest: RestService) {}
@ -13,7 +13,7 @@ export class TenantManagementService {
const request: Rest.Request<null> = {
method: 'GET',
url: '/api/multi-tenancy/tenants',
params,
params
};
return this.rest.request<null, TenantManagement.Response>(request);
@ -22,7 +22,7 @@ export class TenantManagementService {
getTenantById(id: string): Observable<ABP.BasicItem> {
const request: Rest.Request<null> = {
method: 'GET',
url: `/api/multi-tenancy/tenants/${id}`,
url: `/api/multi-tenancy/tenants/${id}`
};
return this.rest.request<null, ABP.BasicItem>(request);
@ -31,7 +31,7 @@ export class TenantManagementService {
deleteTenant(id: string): Observable<null> {
const request: Rest.Request<null> = {
method: 'DELETE',
url: `/api/multi-tenancy/tenants/${id}`,
url: `/api/multi-tenancy/tenants/${id}`
};
return this.rest.request<null, null>(request);
@ -40,8 +40,8 @@ export class TenantManagementService {
createTenant(body: TenantManagement.AddRequest): Observable<ABP.BasicItem> {
const request: Rest.Request<TenantManagement.AddRequest> = {
method: 'POST',
url: `/api/multi-tenancy/tenants`,
body,
url: '/api/multi-tenancy/tenants',
body
};
return this.rest.request<TenantManagement.AddRequest, ABP.BasicItem>(request);
@ -54,7 +54,7 @@ export class TenantManagementService {
const request: Rest.Request<TenantManagement.UpdateRequest> = {
method: 'PUT',
url,
body,
body
};
return this.rest.request<TenantManagement.AddRequest, ABP.BasicItem>(request);
@ -66,7 +66,7 @@ export class TenantManagementService {
const request: Rest.Request<TenantManagement.DefaultConnectionStringRequest> = {
method: 'GET',
responseType: Rest.ResponseType.Text,
url,
url
};
return this.rest.request<TenantManagement.DefaultConnectionStringRequest, string>(request);
}
@ -77,7 +77,7 @@ export class TenantManagementService {
const request: Rest.Request<TenantManagement.DefaultConnectionStringRequest> = {
method: 'PUT',
url,
params: { defaultConnectionString: payload.defaultConnectionString },
params: { defaultConnectionString: payload.defaultConnectionString }
};
return this.rest.request<TenantManagement.DefaultConnectionStringRequest, any>(request);
}
@ -87,7 +87,7 @@ export class TenantManagementService {
const request: Rest.Request<TenantManagement.DefaultConnectionStringRequest> = {
method: 'DELETE',
url,
url
};
return this.rest.request<TenantManagement.DefaultConnectionStringRequest, any>(request);
}

29
npm/ng-packs/packages/theme-basic/src/lib/components/application-layout/application-layout.component.ts

@ -6,7 +6,7 @@ import {
eLayoutType,
SetLanguage,
SessionState,
takeUntilDestroy,
takeUntilDestroy
} from '@abp/ng.core';
import {
AfterViewInit,
@ -16,7 +16,7 @@ import {
TemplateRef,
TrackByFunction,
ViewChild,
ViewChildren,
ViewChildren
} from '@angular/core';
import { NgbDropdown } from '@ng-bootstrap/ng-bootstrap';
import { Navigate, RouterState } from '@ngxs/router-plugin';
@ -32,7 +32,7 @@ import { LayoutState } from '../../states';
@Component({
selector: 'abp-layout-application',
templateUrl: './application-layout.component.html',
templateUrl: './application-layout.component.html'
})
export class ApplicationLayoutComponent implements AfterViewInit, OnDestroy {
// required for dynamic component
@ -59,9 +59,9 @@ export class ApplicationLayoutComponent implements AfterViewInit, OnDestroy {
@ViewChildren('navbarRootDropdown', { read: NgbDropdown })
navbarRootDropdowns: QueryList<NgbDropdown>;
isOpenChangePassword: boolean = false;
isOpenChangePassword = false;
isOpenProfile: boolean = false;
isOpenProfile = false;
isDropdownChildDynamic: boolean;
@ -71,16 +71,13 @@ export class ApplicationLayoutComponent implements AfterViewInit, OnDestroy {
get defaultLanguage$(): Observable<string> {
return this.languages$.pipe(
map(
languages => snq(() => languages.find(lang => lang.cultureName === this.selectedLangCulture).displayName),
'',
),
map(languages => snq(() => languages.find(lang => lang.cultureName === this.selectedLangCulture).displayName), '')
);
}
get dropdownLanguages$(): Observable<ApplicationConfiguration.Language[]> {
return this.languages$.pipe(
map(languages => snq(() => languages.filter(lang => lang.cultureName !== this.selectedLangCulture)), []),
map(languages => snq(() => languages.filter(lang => lang.cultureName !== this.selectedLangCulture)), [])
);
}
@ -116,8 +113,8 @@ export class ApplicationLayoutComponent implements AfterViewInit, OnDestroy {
this.store.dispatch(
new AddNavigationElement([
{ element: this.languageRef, order: 4, name: 'LanguageRef' },
{ element: this.currentUserRef, order: 5, name: 'CurrentUserRef' },
]),
{ element: this.currentUserRef, order: 5, name: 'CurrentUserRef' }
])
);
}
@ -125,7 +122,7 @@ export class ApplicationLayoutComponent implements AfterViewInit, OnDestroy {
.pipe(
map(elements => elements.map(({ element }) => element)),
filter(elements => !compare(elements, this.rightPartElements)),
takeUntilDestroy(this),
takeUntilDestroy(this)
)
.subscribe(elements => {
setTimeout(() => (this.rightPartElements = elements), 0);
@ -136,7 +133,7 @@ export class ApplicationLayoutComponent implements AfterViewInit, OnDestroy {
fromEvent(window, 'resize')
.pipe(
takeUntilDestroy(this),
debounceTime(250),
debounceTime(250)
)
.subscribe(() => {
this.checkWindowWidth();
@ -153,8 +150,8 @@ export class ApplicationLayoutComponent implements AfterViewInit, OnDestroy {
this.oauthService.logOut();
this.store.dispatch(
new Navigate(['/'], null, {
state: { redirectUrl: this.store.selectSnapshot(RouterState).state.url },
}),
state: { redirectUrl: this.store.selectSnapshot(RouterState).state.url }
})
);
this.store.dispatch(new GetAppConfiguration());
}

4
npm/ng-packs/packages/theme-basic/src/lib/components/layout/layout.component.ts

@ -6,10 +6,10 @@ import { Store } from '@ngxs/store';
@Component({
selector: ' abp-layout',
templateUrl: './layout.component.html',
animations: [slideFromBottom],
animations: [slideFromBottom]
})
export class LayoutComponent {
isCollapsed: boolean = true;
isCollapsed = true;
get appInfo(): Config.Application {
return this.store.selectSnapshot(ConfigState.getApplicationInfo);

2
npm/ng-packs/packages/theme-basic/src/lib/constants/styles.ts

@ -6,4 +6,4 @@ export default `
.entry-row {
margin-bottom: 15px;
}
`;
`;

2
npm/ng-packs/packages/theme-basic/src/lib/states/index.ts

@ -1 +1 @@
export * from "./layout.state";
export * from './layout.state';

20
npm/ng-packs/packages/theme-basic/src/lib/theme-basic.module.ts

@ -28,19 +28,19 @@ export const LAYOUTS = [ApplicationLayoutComponent, AccountLayoutComponent, Empt
NgxValidateCoreModule.forRoot({
targetSelector: '.form-group',
blueprints: {
email: `AbpAccount::ThisFieldIsNotAValidEmailAddress.`,
max: `AbpAccount::ThisFieldMustBeBetween{0}And{1}[{{ min }},{{ max }}]`,
maxlength: `AbpAccount::ThisFieldMustBeAStringWithAMaximumLengthOf{1}[{{ requiredLength }}]`,
min: `AbpAccount::ThisFieldMustBeBetween{0}And{1}[{{ min }},{{ max }}]`,
minlength: `AbpAccount::ThisFieldMustBeAStringOrArrayTypeWithAMinimumLengthOf[{{ min }},{{ max }}]`,
required: `AbpAccount::ThisFieldIsRequired.`,
passwordMismatch: `AbpIdentity::Identity.PasswordConfirmationFailed`,
email: 'AbpAccount::ThisFieldIsNotAValidEmailAddress.',
max: 'AbpAccount::ThisFieldMustBeBetween{0}And{1}[{{ min }},{{ max }}]',
maxlength: 'AbpAccount::ThisFieldMustBeAStringWithAMaximumLengthOf{1}[{{ requiredLength }}]',
min: 'AbpAccount::ThisFieldMustBeBetween{0}And{1}[{{ min }},{{ max }}]',
minlength: 'AbpAccount::ThisFieldMustBeAStringOrArrayTypeWithAMinimumLengthOf[{{ min }},{{ max }}]',
required: 'AbpAccount::ThisFieldIsRequired.',
passwordMismatch: 'AbpIdentity::Identity.PasswordConfirmationFailed'
},
errorTemplate: ValidationErrorComponent,
}),
errorTemplate: ValidationErrorComponent
})
],
exports: [...LAYOUTS],
entryComponents: [...LAYOUTS, ValidationErrorComponent],
entryComponents: [...LAYOUTS, ValidationErrorComponent]
})
export class ThemeBasicModule {
constructor(private initialService: InitialService) {}

4
npm/ng-packs/packages/theme-shared/package.json

@ -10,8 +10,8 @@
"chart.js": "^2.8.0",
"font-awesome": "^4.7.0",
"ngx-perfect-scrollbar": "^8.0.0",
"primeicons": "^1.0.0",
"primeng": "^8.0.0"
"primeicons": "^2.0.0",
"primeng": "^8.0.3"
},
"publishConfig": {
"access": "public"

8
npm/ng-packs/packages/theme-shared/src/lib/abstracts/toaster.ts

@ -2,12 +2,12 @@ import { MessageService } from 'primeng/components/common/messageservice';
import { Observable, Subject } from 'rxjs';
import { Toaster } from '../models/toaster';
export class AbstractToaster<T = Toaster.Options> {
export abstract class AbstractToaster<T = Toaster.Options> {
status$: Subject<Toaster.Status>;
key: string = 'abpToast';
key = 'abpToast';
sticky: boolean = false;
sticky = false;
constructor(protected messageService: MessageService) {}
@ -36,7 +36,7 @@ export class AbstractToaster<T = Toaster.Options> {
summary: title || '',
...options,
key: this.key,
...(typeof (options || ({} as any)).sticky === 'undefined' && { sticky: this.sticky }),
...(typeof (options || ({} as any)).sticky === 'undefined' && { sticky: this.sticky })
});
this.status$ = new Subject<Toaster.Status>();
return this.status$;

10
npm/ng-packs/packages/theme-shared/src/lib/animations/collapse.animations.ts

@ -5,15 +5,15 @@ export const collapse = trigger('collapse', [
'open',
style({
height: '*',
overflow: 'hidden',
}),
overflow: 'hidden'
})
),
state(
'close',
style({
height: '0px',
overflow: 'hidden',
}),
overflow: 'hidden'
})
),
transition(`open <=> close`, animate('{{duration}}ms'), { params: { duration: '350' } }),
transition('open <=> close', animate('{{duration}}ms'), { params: { duration: '350' } })
]);

13
npm/ng-packs/packages/theme-shared/src/lib/components/breadcrumb/breadcrumb.component.html

@ -0,0 +1,13 @@
<ol *ngIf="show" class="breadcrumb">
<li class="breadcrumb-item">
<a routerLink="/"><i class="fa fa-home"></i> </a>
</li>
<li
*ngFor="let segment of segments; let last = last"
class="breadcrumb-item"
[class.active]="last"
aria-current="page"
>
{{ segment | abpLocalization }}
</li>
</ol>

16
npm/ng-packs/packages/theme-shared/src/lib/components/breadcrumb/breadcrumb.component.ts

@ -5,21 +5,7 @@ import { ConfigState, ABP } from '@abp/ng.core';
@Component({
selector: 'abp-breadcrumb',
template: `
<ol *ngIf="show" class="breadcrumb">
<li class="breadcrumb-item">
<a routerLink="/"><i class="fa fa-home"></i> </a>
</li>
<li
*ngFor="let segment of segments; let last = last"
class="breadcrumb-item"
[class.active]="last"
aria-current="page"
>
{{ segment | abpLocalization }}
</li>
</ol>
`,
templateUrl: './breadcrumb.component.html'
})
export class BreadcrumbComponent implements OnInit {
show: boolean;

10
npm/ng-packs/packages/theme-shared/src/lib/components/button/button.component.ts

@ -6,23 +6,23 @@ import { Component, Input } from '@angular/core';
<button [attr.type]="type" [ngClass]="buttonClass" [disabled]="loading || disabled">
<i [ngClass]="icon" class="mr-1"></i><ng-content></ng-content>
</button>
`,
`
})
export class ButtonComponent {
@Input()
buttonClass: string = 'btn btn-primary';
buttonClass = 'btn btn-primary';
@Input()
type: string = 'button';
type = 'button';
@Input()
iconClass: string;
@Input()
loading: boolean = false;
loading = false;
@Input()
disabled: boolean = false;
disabled = false;
get icon(): string {
return `${this.loading ? 'fa fa-pulse fa-spinner' : this.iconClass || 'd-none'}`;

27
npm/ng-packs/packages/theme-shared/src/lib/components/change-password/change-password.component.ts

@ -8,7 +8,7 @@ import {
Output,
SimpleChanges,
TemplateRef,
ViewChild,
ViewChild
} from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { comparePasswords, Validation } from '@ngx-validate/core';
@ -23,7 +23,7 @@ const PASSWORD_FIELDS = ['newPassword', 'repeatNewPassword'];
@Component({
selector: 'abp-change-password',
templateUrl: './change-password.component.html',
templateUrl: './change-password.component.html'
})
export class ChangePasswordComponent implements OnInit, OnChanges {
protected _visible;
@ -38,17 +38,16 @@ export class ChangePasswordComponent implements OnInit, OnChanges {
this.visibleChange.emit(value);
}
@Output()
visibleChange = new EventEmitter<boolean>();
@Output() readonly visibleChange = new EventEmitter<boolean>();
@ViewChild('modalContent', { static: false })
modalContent: TemplateRef<any>;
form: FormGroup;
modalBusy: boolean = false;
modalBusy = false;
mapErrorsFn: Validation.MapErrorsFn = function(errors, groupErrors, control) {
mapErrorsFn: Validation.MapErrorsFn = (errors, groupErrors, control) => {
if (PASSWORD_FIELDS.indexOf(control.name) < 0) return errors;
return errors.concat(groupErrors.filter(({ key }) => key === 'passwordMismatch'));
@ -61,11 +60,11 @@ export class ChangePasswordComponent implements OnInit, OnChanges {
{
password: ['', required],
newPassword: ['', required],
repeatNewPassword: ['', required],
repeatNewPassword: ['', required]
},
{
validators: [comparePasswords(PASSWORD_FIELDS)],
},
validators: [comparePasswords(PASSWORD_FIELDS)]
}
);
}
@ -77,13 +76,13 @@ export class ChangePasswordComponent implements OnInit, OnChanges {
.dispatch(
new ChangePassword({
currentPassword: this.form.get('password').value,
newPassword: this.form.get('newPassword').value,
}),
newPassword: this.form.get('newPassword').value
})
)
.pipe(
finalize(() => {
this.modalBusy = false;
}),
})
)
.subscribe({
next: () => {
@ -92,9 +91,9 @@ export class ChangePasswordComponent implements OnInit, OnChanges {
},
error: err => {
this.toasterService.error(snq(() => err.error.error.message, 'AbpAccount::DefaultErrorMessage'), 'Error', {
life: 7000,
life: 7000
});
},
}
});
}

26
npm/ng-packs/packages/theme-shared/src/lib/components/chart/chart.component.ts

@ -6,7 +6,7 @@ import {
Input,
OnDestroy,
Output,
ChangeDetectorRef,
ChangeDetectorRef
} from '@angular/core';
import { BehaviorSubject } from 'rxjs';
import { chartJsLoaded$ } from '../../utils/widget-utils';
@ -14,7 +14,7 @@ declare const Chart: any;
@Component({
selector: 'abp-chart',
templateUrl: './chart.component.html',
templateUrl: './chart.component.html'
})
export class ChartComponent implements AfterViewInit, OnDestroy {
@Input() type: string;
@ -27,11 +27,12 @@ export class ChartComponent implements AfterViewInit, OnDestroy {
@Input() height: string;
@Input() responsive: boolean = true;
@Input() responsive = true;
@Output() onDataSelect: EventEmitter<any> = new EventEmitter();
// tslint:disable-next-line: no-output-on-prefix
@Output() readonly onDataSelect: EventEmitter<any> = new EventEmitter();
@Output() initialized = new BehaviorSubject(this);
@Output() readonly initialized = new BehaviorSubject(this);
private _initialized: boolean;
@ -61,6 +62,7 @@ export class ChartComponent implements AfterViewInit, OnDestroy {
ngAfterViewInit() {
chartJsLoaded$.subscribe(() => {
try {
// tslint:disable-next-line: no-unused-expression
Chart;
} catch (error) {
console.error(`Chart is not found. Import the Chart from app.module like shown below:
@ -76,16 +78,20 @@ export class ChartComponent implements AfterViewInit, OnDestroy {
onCanvasClick = event => {
if (this.chart) {
let element = this.chart.getElementAtEvent(event);
let dataset = this.chart.getDatasetAtEvent(event);
const element = this.chart.getElementAtEvent(event);
const dataset = this.chart.getDatasetAtEvent(event);
if (element && element[0] && dataset) {
this.onDataSelect.emit({ originalEvent: event, element: element[0], dataset: dataset });
this.onDataSelect.emit({
originalEvent: event,
element: element[0],
dataset
});
}
}
};
initChart = () => {
let opts = this.options || {};
const opts = this.options || {};
opts.responsive = this.responsive;
// allows chart to resize in responsive mode
@ -97,7 +103,7 @@ export class ChartComponent implements AfterViewInit, OnDestroy {
type: this.type,
data: this.data,
options: this.options,
plugins: this.plugins,
plugins: this.plugins
});
this.cdRef.detectChanges();

12
npm/ng-packs/packages/theme-shared/src/lib/components/confirmation/confirmation.component.ts

@ -4,6 +4,7 @@ import { Toaster } from '../../models/toaster';
@Component({
selector: 'abp-confirmation',
// tslint:disable-next-line: component-max-inline-declarations
template: `
<p-toast
position="center"
@ -23,11 +24,18 @@ import { Toaster } from '../../models/toaster';
</div>
<div class="abp-confirm-footer justify-content-center">
<button *ngIf="!message.hideCancelBtn" type="button" class="btn btn-sm btn-primary" (click)="close(reject)">
<button
*ngIf="!message.hideCancelBtn"
id="cancel"
type="button"
class="btn btn-sm btn-primary"
(click)="close(reject)"
>
{{ message.cancelCopy || 'AbpIdentity::Cancel' | abpLocalization }}
</button>
<button
*ngIf="!message.hideYesBtn"
id="confirm"
type="button"
class="btn btn-sm btn-primary"
(click)="close(confirm)"
@ -38,7 +46,7 @@ import { Toaster } from '../../models/toaster';
</div>
</ng-template>
</p-toast>
`,
`
})
export class ConfirmationComponent {
confirm = Toaster.Status.confirm;

22
npm/ng-packs/packages/theme-shared/src/lib/components/error/error.component.html

@ -0,0 +1,22 @@
<div class="error">
<button id="abp-close-button mr-4" type="button" class="close" (click)="destroy()">
<span aria-hidden="true">&times;</span>
</button>
<div class="row centered">
<div class="col-md-12">
<div class="error-template">
<h1>
{{ title | abpLocalization }}
</h1>
<div class="error-details">
{{ details | abpLocalization }}
</div>
<div class="error-actions">
<a (click)="destroy()" routerLink="/" class="btn btn-primary btn-md mt-2"
><span class="glyphicon glyphicon-home"></span> {{ '::Menu:Home' | abpLocalization }}
</a>
</div>
</div>
</div>
</div>
</div>

0
npm/ng-packs/packages/theme-shared/src/lib/components/errors/error.component.scss → npm/ng-packs/packages/theme-shared/src/lib/components/error/error.component.scss

22
npm/ng-packs/packages/theme-shared/src/lib/components/error/error.component.ts

@ -0,0 +1,22 @@
import { Component, Renderer2, ElementRef } from '@angular/core';
@Component({
selector: 'abp-error',
templateUrl: './error.component.html',
styleUrls: ['error.component.scss']
})
export class ErrorComponent {
title = 'Oops!';
details = 'Sorry, an error has occured.';
renderer: Renderer2;
elementRef: ElementRef;
host: any;
destroy() {
this.renderer.removeChild(this.host, this.elementRef.nativeElement);
}
}

45
npm/ng-packs/packages/theme-shared/src/lib/components/errors/error.component.ts

@ -1,45 +0,0 @@
import { Component, Renderer2, ElementRef } from '@angular/core';
@Component({
selector: 'abp-error',
template: `
<div class="error">
<button id="abp-close-button mr-2" type="button" class="close" (click)="destroy()">
<span aria-hidden="true">&times;</span>
</button>
<div class="row centered">
<div class="col-md-12">
<div class="error-template">
<h1>
{{ title | abpLocalization }}
</h1>
<div class="error-details">
{{ details | abpLocalization }}
</div>
<div class="error-actions">
<a (click)="destroy()" routerLink="/" class="btn btn-primary btn-md mt-2"
><span class="glyphicon glyphicon-home"></span> {{ '::Menu:Home' | abpLocalization }}
</a>
</div>
</div>
</div>
</div>
</div>
`,
styleUrls: ['error.component.scss'],
})
export class ErrorComponent {
title = 'Oops!';
details = 'Sorry, an error has occured.';
renderer: Renderer2;
elementRef: ElementRef;
host: any;
destroy() {
this.renderer.removeChild(this.host, this.elementRef.nativeElement);
}
}

43
npm/ng-packs/packages/theme-shared/src/lib/components/loader-bar/loader-bar.component.ts

@ -20,27 +20,9 @@ import { filter } from 'rxjs/operators';
></div>
</div>
`,
styleUrls: ['./loader-bar.component.scss'],
styleUrls: ['./loader-bar.component.scss']
})
export class LoaderBarComponent implements OnDestroy {
@Input()
containerClass: string = 'abp-loader-bar';
@Input()
color: string = '#77b6ff';
@Input()
isLoading: boolean = false;
@Input()
filter = (action: StartLoader | StopLoader) => action.payload.url.indexOf('openid-configuration') < 0;
progressLevel: number = 0;
interval: Subscription;
timer: Subscription;
get boxShadow(): string {
return `0 0 10px rgba(${this.color}, 0.5)`;
}
@ -50,7 +32,7 @@ export class LoaderBarComponent implements OnDestroy {
.pipe(
ofActionSuccessful(StartLoader, StopLoader),
filter(this.filter),
takeUntilDestroy(this),
takeUntilDestroy(this)
)
.subscribe(action => {
if (action instanceof StartLoader) this.startLoading();
@ -61,15 +43,32 @@ export class LoaderBarComponent implements OnDestroy {
.pipe(
filter(
event =>
event instanceof NavigationStart || event instanceof NavigationEnd || event instanceof NavigationError,
event instanceof NavigationStart || event instanceof NavigationEnd || event instanceof NavigationError
),
takeUntilDestroy(this),
takeUntilDestroy(this)
)
.subscribe(event => {
if (event instanceof NavigationStart) this.startLoading();
else this.stopLoading();
});
}
@Input()
containerClass = 'abp-loader-bar';
@Input()
color = '#77b6ff';
@Input()
isLoading = false;
progressLevel = 0;
interval: Subscription;
timer: Subscription;
@Input()
filter = (action: StartLoader | StopLoader) => action.payload.url.indexOf('openid-configuration') < 0;
ngOnDestroy() {
this.interval.unsubscribe();

48
npm/ng-packs/packages/theme-shared/src/lib/components/modal/modal.component.ts

@ -9,7 +9,7 @@ import {
Renderer2,
TemplateRef,
ViewChild,
ViewChildren,
ViewChildren
} from '@angular/core';
import { fromEvent, Subject, timer } from 'rxjs';
import { filter, take, takeUntil, debounceTime } from 'rxjs/operators';
@ -23,7 +23,7 @@ const ANIMATION_TIMEOUT = 200;
@Component({
selector: 'abp-modal',
templateUrl: './modal.component.html',
templateUrl: './modal.component.html'
})
export class ModalComponent implements OnDestroy {
@Input()
@ -51,7 +51,7 @@ export class ModalComponent implements OnDestroy {
this.renderer.addClass(this.modalContent.nativeElement, 'fade-out-top');
setTimeout(() => {
this.setVisible(value);
this.ngOnDestroy();
this.destroy$.next();
}, ANIMATION_TIMEOUT - 10);
}
}
@ -68,9 +68,9 @@ export class ModalComponent implements OnDestroy {
this._busy = value;
}
@Input() centered: boolean = false;
@Input() centered = false;
@Input() modalClass: string = '';
@Input() modalClass = '';
@Input() size: ModalSize = 'lg';
@ -78,9 +78,9 @@ export class ModalComponent implements OnDestroy {
@Input() minHeight: number;
@Output() visibleChange = new EventEmitter<boolean>();
@Output() readonly visibleChange = new EventEmitter<boolean>();
@Output() init = new EventEmitter<void>();
@Output() readonly init = new EventEmitter<void>();
@ContentChild('abpHeader', { static: false }) abpHeader: TemplateRef<any>;
@ -88,29 +88,29 @@ export class ModalComponent implements OnDestroy {
@ContentChild('abpFooter', { static: false }) abpFooter: TemplateRef<any>;
@ContentChild('abpClose', { static: false, read: ElementRef }) abpClose: ElementRef<any>;
@ContentChild('abpClose', { static: false, read: ElementRef })
abpClose: ElementRef<any>;
@ContentChild(ButtonComponent, { static: false, read: ButtonComponent }) abpSubmit: ButtonComponent;
@ContentChild(ButtonComponent, { static: false, read: ButtonComponent })
abpSubmit: ButtonComponent;
@ViewChild('abpModalContent', { static: false }) modalContent: ElementRef;
@ViewChildren('abp-button') abpButtons;
@Output()
show = new EventEmitter();
@Output() readonly appear = new EventEmitter();
@Output()
hide = new EventEmitter();
@Output() readonly disappear = new EventEmitter();
_visible: boolean = false;
_visible = false;
_busy: boolean = false;
_busy = false;
showModal: boolean = false;
showModal = false;
isOpenConfirmation: boolean = false;
isOpenConfirmation = false;
closable: boolean = false;
closable = false;
destroy$ = new Subject<void>();
@ -131,11 +131,11 @@ export class ModalComponent implements OnDestroy {
.subscribe(_ => (this.closable = true));
this.renderer.addClass(document.body, 'modal-open');
this.show.emit();
this.appear.emit();
} else {
this.closable = false;
this.renderer.removeClass(document.body, 'modal-open');
this.hide.emit();
this.disappear.emit();
}
}
@ -144,7 +144,7 @@ export class ModalComponent implements OnDestroy {
.pipe(
takeUntil(this.destroy$),
debounceTime(150),
filter((key: KeyboardEvent) => key && key.code === 'Escape' && this.closable),
filter((key: KeyboardEvent) => key && key.code === 'Escape' && this.closable)
)
.subscribe(_ => {
this.close();
@ -155,7 +155,7 @@ export class ModalComponent implements OnDestroy {
fromEvent(this.abpClose.nativeElement, 'click')
.pipe(
takeUntil(this.destroy$),
filter(() => !!(this.closable && this.modalContent)),
filter(() => !!(this.closable && this.modalContent))
)
.subscribe(() => this.close());
}, 0);
@ -167,7 +167,7 @@ export class ModalComponent implements OnDestroy {
if (!this.closable || this.busy) return;
const nodes = getFlatNodes(
(this.modalContent.nativeElement.querySelector('#abp-modal-body') as HTMLElement).childNodes,
(this.modalContent.nativeElement.querySelector('#abp-modal-body') as HTMLElement).childNodes
);
if (hasNgDirty(nodes)) {
@ -194,7 +194,7 @@ export class ModalComponent implements OnDestroy {
function getFlatNodes(nodes: NodeList): HTMLElement[] {
return Array.from(nodes).reduce(
(acc, val) => [...acc, ...(val.childNodes && val.childNodes.length ? getFlatNodes(val.childNodes) : [val])],
[],
[]
);
}

11
npm/ng-packs/packages/theme-shared/src/lib/components/profile/profile.component.ts

@ -9,7 +9,7 @@ const { maxLength, required, email } = Validators;
@Component({
selector: 'abp-profile',
templateUrl: './profile.component.html',
templateUrl: './profile.component.html'
})
export class ProfileComponent implements OnChanges {
protected _visible;
@ -24,15 +24,14 @@ export class ProfileComponent implements OnChanges {
this.visibleChange.emit(value);
}
@Output()
visibleChange = new EventEmitter<boolean>();
@Output() readonly visibleChange = new EventEmitter<boolean>();
@Select(ProfileState.getProfile)
profile$: Observable<Profile.Response>;
form: FormGroup;
modalBusy: boolean = false;
modalBusy = false;
constructor(private fb: FormBuilder, private store: Store) {}
@ -41,7 +40,7 @@ export class ProfileComponent implements OnChanges {
.dispatch(new GetProfile())
.pipe(
withLatestFrom(this.profile$),
take(1),
take(1)
)
.subscribe(([, profile]) => {
this.form = this.fb.group({
@ -49,7 +48,7 @@ export class ProfileComponent implements OnChanges {
email: [profile.email, [required, email, maxLength(256)]],
name: [profile.name || '', [maxLength(64)]],
surname: [profile.surname || '', [maxLength(64)]],
phoneNumber: [profile.phoneNumber || '', [maxLength(16)]],
phoneNumber: [profile.phoneNumber || '', [maxLength(16)]]
});
});
}

7
npm/ng-packs/packages/theme-shared/src/lib/components/table-empty-message/table-empty-message.component.ts

@ -1,12 +1,13 @@
import { Component, OnInit, Input } from '@angular/core';
@Component({
// tslint:disable-next-line: component-selector
selector: '[abp-table-empty-message]',
template: `
<td class="text-center" [attr.colspan]="colspan">
{{ emptyMessage | abpLocalization }}
</td>
`,
`
})
export class TableEmptyMessageComponent {
@Input()
@ -16,10 +17,10 @@ export class TableEmptyMessageComponent {
message: string;
@Input()
localizationResource: string = 'AbpAccount';
localizationResource = 'AbpAccount';
@Input()
localizationProp: string = 'NoDataAvailableInDatatable';
localizationProp = 'NoDataAvailableInDatatable';
get emptyMessage(): string {
return this.message || `${this.localizationResource}::${this.localizationProp}`;

3
npm/ng-packs/packages/theme-shared/src/lib/components/toast/toast.component.ts

@ -2,6 +2,7 @@ import { Component } from '@angular/core';
@Component({
selector: 'abp-toast',
// tslint:disable-next-line: component-max-inline-declarations
template: `
<p-toast position="bottom-right" key="abpToast" styleClass="abp-toast" [baseZIndex]="1000">
<ng-template let-message pTemplate="message">
@ -20,6 +21,6 @@ import { Component } from '@angular/core';
</div>
</ng-template>
</p-toast>
`,
`
})
export class ToastComponent {}

2
npm/ng-packs/packages/theme-shared/src/lib/contants/scripts.ts

@ -1 +1 @@
export default ``;
export default '';

55
npm/ng-packs/packages/theme-shared/src/lib/handlers/error.handler.ts

@ -11,31 +11,36 @@ import {
import { Navigate, RouterState } from '@ngxs/router-plugin';
import { Actions, ofActionSuccessful, Store } from '@ngxs/store';
import { Observable } from 'rxjs';
import { ErrorComponent } from '../components/errors/error.component';
import { ErrorComponent } from '../components/error/error.component';
import { Toaster } from '../models/toaster';
import { ConfirmationService } from '../services/confirmation.service';
import snq from 'snq';
const DEFAULTS = {
export const DEFAULT_ERROR_MESSAGES = {
defaultError: {
message: 'An error has occurred!',
title: 'An error has occurred!',
details: 'Error detail not sent by server.',
},
defaultError401: {
message: 'You are not authenticated!',
title: 'You are not authenticated!',
details: 'You should be authenticated (sign in) in order to perform this operation.',
},
defaultError403: {
message: 'You are not authorized!',
title: 'You are not authorized!',
details: 'You are not allowed to perform this operation.',
},
defaultError404: {
message: 'Resource not found!',
title: 'Resource not found!',
details: 'The resource requested could not found on the server.',
},
defaultError500: {
title: '500',
details: 'AbpAccount::InternalServerErrorMessage',
},
defaultErrorUnknown: {
title: 'Unknown Error',
details: 'AbpAccount::InternalServerErrorMessage',
},
};
@Injectable({ providedIn: 'root' })
@ -51,7 +56,7 @@ export class ErrorHandler {
) {
actions.pipe(ofActionSuccessful(RestOccurError)).subscribe(res => {
const { payload: err = {} as HttpErrorResponse | any } = res;
const body = snq(() => (err as HttpErrorResponse).error.error, DEFAULTS.defaultError.message);
const body = snq(() => (err as HttpErrorResponse).error.error, DEFAULT_ERROR_MESSAGES.defaultError.title);
if (err instanceof HttpErrorResponse && err.headers.get('_AbpErrorFormat')) {
const confirmation$ = this.showError(null, null, body);
@ -64,35 +69,39 @@ export class ErrorHandler {
} else {
switch ((err as HttpErrorResponse).status) {
case 401:
this.showError(DEFAULTS.defaultError401.details, DEFAULTS.defaultError401.message).subscribe(() =>
this.navigateToLogin(),
);
this.showError(
DEFAULT_ERROR_MESSAGES.defaultError401.details,
DEFAULT_ERROR_MESSAGES.defaultError401.title,
).subscribe(() => this.navigateToLogin());
break;
case 403:
this.createErrorComponent({
title: DEFAULTS.defaultError403.message,
details: DEFAULTS.defaultError403.details,
title: DEFAULT_ERROR_MESSAGES.defaultError403.title,
details: DEFAULT_ERROR_MESSAGES.defaultError403.details,
});
break;
case 404:
this.showError(DEFAULTS.defaultError404.details, DEFAULTS.defaultError404.message);
this.showError(
DEFAULT_ERROR_MESSAGES.defaultError404.details,
DEFAULT_ERROR_MESSAGES.defaultError404.title,
);
break;
case 500:
this.createErrorComponent({
title: '500',
details: 'AbpAccount::InternalServerErrorMessage',
title: DEFAULT_ERROR_MESSAGES.defaultError500.title,
details: DEFAULT_ERROR_MESSAGES.defaultError500.details,
});
break;
case 0:
if ((err as HttpErrorResponse).statusText === 'Unknown Error') {
this.createErrorComponent({
title: 'Unknown Error',
details: 'AbpAccount::InternalServerErrorMessage',
title: DEFAULT_ERROR_MESSAGES.defaultErrorUnknown.title,
details: DEFAULT_ERROR_MESSAGES.defaultErrorUnknown.details,
});
}
break;
default:
this.showError(DEFAULTS.defaultError.details, DEFAULTS.defaultError.message);
this.showError(DEFAULT_ERROR_MESSAGES.defaultError.details, DEFAULT_ERROR_MESSAGES.defaultError.title);
break;
}
}
@ -105,7 +114,7 @@ export class ErrorHandler {
message = body.details;
title = body.message;
} else {
message = body.message || DEFAULTS.defaultError.message;
message = body.message || DEFAULT_ERROR_MESSAGES.defaultError.title;
}
}
@ -125,7 +134,7 @@ export class ErrorHandler {
createErrorComponent(instance: Partial<ErrorComponent>) {
const renderer = this.rendererFactory.createRenderer(null, null);
const host = renderer.selectRootElement('app-root', true);
const host = renderer.selectRootElement(document.body, true);
const componentRef = this.cfRes.resolveComponentFactory(ErrorComponent).create(this.injector);

8
npm/ng-packs/packages/theme-shared/src/lib/services/confirmation.service.ts

@ -8,9 +8,9 @@ import { Toaster } from '../models/toaster';
@Injectable({ providedIn: 'root' })
export class ConfirmationService extends AbstractToaster<Confirmation.Options> {
key: string = 'abpConfirmation';
key = 'abpConfirmation';
sticky: boolean = true;
sticky = true;
destroy$ = new Subject();
@ -22,7 +22,7 @@ export class ConfirmationService extends AbstractToaster<Confirmation.Options> {
message: string,
title: string,
severity: Toaster.Severity,
options?: Confirmation.Options,
options?: Confirmation.Options
): Observable<Toaster.Status> {
this.listenToEscape();
@ -40,7 +40,7 @@ export class ConfirmationService extends AbstractToaster<Confirmation.Options> {
.pipe(
takeUntil(this.destroy$),
debounceTime(150),
filter((key: KeyboardEvent) => key && key.code === 'Escape'),
filter((key: KeyboardEvent) => key && key.code === 'Escape')
)
.subscribe(_ => {
this.clear();

54
npm/ng-packs/packages/theme-shared/src/lib/tests/button.component.spec.ts

@ -0,0 +1,54 @@
import { CoreModule } from '@abp/ng.core';
import {
createComponentFactory,
createHostFactory,
Spectator,
SpectatorHost,
createTestComponentFactory
} from '@ngneat/spectator';
import { ButtonComponent } from '../components';
describe('ButtonComponent', () => {
let host: SpectatorHost<ButtonComponent>;
const createHost = createHostFactory(ButtonComponent);
beforeEach(() => (host = createHost('<abp-button iconClass="fa fa-check">Button</abp-button>')));
it('should display the button', () => {
expect(host.query('button')).toBeTruthy();
});
it('should equal the default classes to btn btn-primary', () => {
expect(host.query('button')).toHaveClass('btn btn-primary');
});
it('should equal the default type to button', () => {
expect(host.query('button')).toHaveAttribute('type', 'button');
});
it('should enabled', () => {
expect(host.query('[disabled]')).toBeFalsy();
});
it('should have the text content', () => {
expect(host.query('button')).toHaveText('Button');
});
it('should display the icon', () => {
expect(host.query('i.d-none')).toBeFalsy();
expect(host.query('i')).toHaveClass('fa');
});
it('should display the spinner icon', () => {
host.component.loading = true;
host.detectComponentChanges();
expect(host.query('i')).toHaveClass('fa-spinner');
});
it('should disabled when the loading input is true', () => {
host.component.loading = true;
host.detectComponentChanges();
expect(host.query('[disabled]')).toBeDefined();
});
});

109
npm/ng-packs/packages/theme-shared/src/lib/tests/error.handler.spec.ts

@ -0,0 +1,109 @@
import { CoreModule, RestOccurError, RouterOutletComponent } from '@abp/ng.core';
import { Location } from '@angular/common';
import { HttpErrorResponse, HttpHeaders } from '@angular/common/http';
import { Component } from '@angular/core';
import { createRoutingFactory, SpectatorRouting } from '@ngneat/spectator';
import { RouterState } from '@ngxs/router-plugin';
import { NgxsModule, Store } from '@ngxs/store';
import { NgxsResetPluginModule, StateOverwrite } from 'ngxs-reset-plugin';
import { DEFAULT_ERROR_MESSAGES, ErrorHandler } from '../handlers';
import { ThemeSharedModule } from '../theme-shared.module';
@Component({ selector: 'abp-dummy', template: 'dummy works! <abp-confirmation></abp-confirmation>' })
class DummyComponent {
constructor(public errorHandler: ErrorHandler, public store: Store) {}
}
describe('With Custom Host Component', () => {
let component: SpectatorRouting<DummyComponent>;
const createComponent = createRoutingFactory({
component: DummyComponent,
imports: [CoreModule, ThemeSharedModule.forRoot(), NgxsModule.forRoot([]), NgxsResetPluginModule.forRoot()],
stubsEnabled: false,
routes: [{ path: '', component: DummyComponent }, { path: 'account/login', component: RouterOutletComponent }]
});
beforeEach(() => {
component = createComponent();
const abpError = document.querySelector('abp-error');
if (abpError) document.body.removeChild(abpError);
});
it('should display the error component when server error occurs', () => {
component.component.store.dispatch(new RestOccurError(new HttpErrorResponse({ status: 500 })));
component.detectChanges();
expect(document.querySelector('.error-template')).toHaveText(DEFAULT_ERROR_MESSAGES.defaultError500.title);
expect(document.querySelector('.error-details')).toHaveText(DEFAULT_ERROR_MESSAGES.defaultError500.details);
});
it('should display the error component when authorize error occurs', () => {
component.component.store.dispatch(new RestOccurError(new HttpErrorResponse({ status: 403 })));
component.detectChanges();
expect(document.querySelector('.error-template')).toHaveText(DEFAULT_ERROR_MESSAGES.defaultError403.title);
expect(document.querySelector('.error-details')).toHaveText(DEFAULT_ERROR_MESSAGES.defaultError403.details);
});
it('should display the error component when unknown error occurs', () => {
component.component.store.dispatch(
new RestOccurError(new HttpErrorResponse({ status: 0, statusText: 'Unknown Error' }))
);
component.detectChanges();
expect(document.querySelector('.error-template')).toHaveText(DEFAULT_ERROR_MESSAGES.defaultErrorUnknown.title);
expect(document.querySelector('.error-details')).toHaveText(DEFAULT_ERROR_MESSAGES.defaultErrorUnknown.details);
});
it('should display the confirmation when not found error occurs', () => {
component.component.store.dispatch(new RestOccurError(new HttpErrorResponse({ status: 404 })));
component.detectChanges();
expect(component.query('.abp-confirm-summary')).toHaveText(DEFAULT_ERROR_MESSAGES.defaultError404.title);
expect(component.query('.abp-confirm-body')).toHaveText(DEFAULT_ERROR_MESSAGES.defaultError404.details);
});
it('should display the confirmation when default error occurs', () => {
component.component.store.dispatch(new RestOccurError(new HttpErrorResponse({ status: 412 })));
component.detectChanges();
expect(component.query('.abp-confirm-summary')).toHaveText(DEFAULT_ERROR_MESSAGES.defaultError.title);
expect(component.query('.abp-confirm-body')).toHaveText(DEFAULT_ERROR_MESSAGES.defaultError.details);
});
it('should display the confirmation when authenticated error occurs', async () => {
component.component.store.dispatch(new RestOccurError(new HttpErrorResponse({ status: 401 })));
component.detectChanges();
component.component.store.dispatch(new StateOverwrite([RouterState, { state: { url: '/' } }]));
component.click('#confirm');
await component.fixture.whenStable();
expect(component.get(Location).path()).toBe('/account/login');
});
it('should display the confirmation when authenticated error occurs with _AbpErrorFormat header', async () => {
let headers: HttpHeaders = new HttpHeaders();
headers = headers.append('_AbpErrorFormat', '_AbpErrorFormat');
component.component.store.dispatch(new RestOccurError(new HttpErrorResponse({ status: 401, headers })));
component.detectChanges();
component.component.store.dispatch(new StateOverwrite([RouterState, { state: { url: '/' } }]));
component.click('#confirm');
await component.fixture.whenStable();
expect(component.get(Location).path()).toBe('/account/login');
});
it('should display the confirmation when error occurs with _AbpErrorFormat header', () => {
let headers: HttpHeaders = new HttpHeaders();
headers = headers.append('_AbpErrorFormat', '_AbpErrorFormat');
component.component.store.dispatch(
new RestOccurError(
new HttpErrorResponse({
error: { error: { message: 'test message', details: 'test detail' } },
status: 412,
headers
})
)
);
component.detectChanges();
expect(component.query('.abp-confirm-summary')).toHaveText('test message');
expect(component.query('.abp-confirm-body')).toHaveText('test detail');
});
});

20
npm/ng-packs/packages/theme-shared/src/lib/theme-shared.module.ts

@ -9,7 +9,7 @@ import { ButtonComponent } from './components/button/button.component';
import { ChangePasswordComponent } from './components/change-password/change-password.component';
import { ChartComponent } from './components/chart/chart.component';
import { ConfirmationComponent } from './components/confirmation/confirmation.component';
import { ErrorComponent } from './components/errors/error.component';
import { ErrorComponent } from './components/error/error.component';
import { LoaderBarComponent } from './components/loader-bar/loader-bar.component';
import { ModalComponent } from './components/modal/modal.component';
import { ProfileComponent } from './components/profile/profile.component';
@ -21,7 +21,7 @@ import { TableEmptyMessageComponent } from './components/table-empty-message/tab
import { NgxValidateCoreModule } from '@ngx-validate/core';
export function appendScript(injector: Injector) {
const fn = function() {
const fn = () => {
import('chart.js').then(() => chartJsLoaded$.next(true));
const lazyLoadService: LazyLoadService = injector.get(LazyLoadService);
@ -32,8 +32,8 @@ export function appendScript(injector: Injector) {
'style',
styles,
'head',
'afterbegin',
) /* lazyLoadService.load(null, 'script', scripts) */,
'afterbegin'
) /* lazyLoadService.load(null, 'script', scripts) */
).pipe(take(1));
};
@ -53,7 +53,7 @@ export function appendScript(injector: Injector) {
ModalComponent,
ProfileComponent,
TableEmptyMessageComponent,
ToastComponent,
ToastComponent
],
exports: [
BreadcrumbComponent,
@ -65,9 +65,9 @@ export function appendScript(injector: Injector) {
ModalComponent,
ProfileComponent,
TableEmptyMessageComponent,
ToastComponent,
ToastComponent
],
entryComponents: [ErrorComponent],
entryComponents: [ErrorComponent]
})
export class ThemeSharedModule {
static forRoot(): ModuleWithProviders {
@ -78,10 +78,10 @@ export class ThemeSharedModule {
provide: APP_INITIALIZER,
multi: true,
deps: [Injector, ErrorHandler],
useFactory: appendScript,
useFactory: appendScript
},
{ provide: MessageService, useClass: MessageService },
],
{ provide: MessageService, useClass: MessageService }
]
};
}
}

14
npm/ng-packs/packages/theme-shared/tslint.json

@ -1,17 +1,7 @@
{
"extends": "../../tslint.json",
"rules": {
"directive-selector": [
true,
"attribute",
"lib",
"camelCase"
],
"component-selector": [
true,
"element",
"lib",
"kebab-case"
]
"directive-selector": [true, "attribute", "abp", "camelCase"],
"component-selector": [true, "element", "abp", "kebab-case"]
}
}

105
npm/ng-packs/tslint.json

@ -1,55 +1,100 @@
{
"extends": "tslint:recommended",
"rulesDirectory": ["node_modules/codelyzer"],
"rules": {
"array-type": false,
"contextual-lifecycle": true,
"component-class-suffix": [true, "Component"],
"directive-class-suffix": [true, "Directive"],
"max-line-length": [true, 140],
"no-consecutive-blank-lines": false,
"no-redundant-jsdoc": true,
"no-var-requires": false,
"object-literal-key-quotes": [true, "as-needed"],
"ordered-imports": false,
"trailing-comma": false,
"component-max-inline-declarations": [true, { "animations": 20, "styles": 10, "template": 10 }],
"no-forward-ref": true,
"no-lifecycle-call": true,
"no-pipe-impure": true,
"no-queries-metadata-property": true,
"no-unused-css": true,
"prefer-output-readonly": true,
"template-conditional-complexity": [true, 4],
"use-component-selector": true,
"max-classes-per-file": false,
"arrow-parens": false,
"arrow-return-shorthand": true,
"callable-types": true,
"class-name": true,
"component-selector": [true, "element", "abp", "kebab-case"],
"curly": false,
"deprecation": {
"severity": "warn"
},
"component-class-suffix": true,
"contextual-lifecycle": true,
"directive-class-suffix": true,
"directive-selector": [true, "attribute", "abp", "camelCase"],
"component-selector": [true, "element", "abp", "kebab-case"],
"forin": true,
"import-blacklist": [true, "rxjs/Rx"],
"interface-name": false,
"max-classes-per-file": false,
"max-line-length": [true, 140],
"member-access": false,
"interface-over-type-literal": true,
"interface-name": [true, "never-prefix"],
"member-access": [true, "no-public"],
"member-ordering": [
true,
{
"order": ["static-field", "instance-field", "static-method", "instance-method"]
}
],
"no-consecutive-blank-lines": false,
"no-arg": true,
"no-bitwise": true,
"no-conflicting-lifecycle": true,
"no-console": [true, "debug", "info", "time", "timeEnd", "trace"],
"no-construct": true,
"no-debugger": true,
"no-duplicate-super": true,
"no-empty-interface": true,
"no-empty": false,
"no-inferrable-types": [false, "ignore-params"],
"no-non-null-assertion": true,
"no-redundant-jsdoc": true,
"no-switch-case-fall-through": true,
"no-use-before-declare": true,
"no-var-requires": false,
"curly": false,
"object-literal-key-quotes": [true, "as-needed"],
"object-literal-sort-keys": false,
"ordered-imports": false,
"quotemark": [true, "single"],
"trailing-comma": false,
"no-conflicting-lifecycle": true,
"no-eval": true,
"no-host-metadata-property": true,
"no-input-rename": true,
"no-inferrable-types": [true, "ignore-params"],
"no-input-rename": false,
"no-inputs-metadata-property": true,
"no-misused-new": true,
"no-namespace": false,
"no-non-null-assertion": true,
"no-output-native": true,
"no-output-on-prefix": true,
"no-output-rename": true,
"no-output-rename": false,
"no-outputs-metadata-property": true,
"no-namespace": false,
"template-banana-in-box": true,
"template-no-negated-async": true,
"no-shadowed-variable": true,
"no-string-literal": false,
"no-string-throw": true,
"no-switch-case-fall-through": true,
"no-unnecessary-initializer": true,
"no-unnecessary-semicolons": false,
"no-unused-expression": true,
"no-var-keyword": true,
"object-literal-sort-keys": false,
"prefer-const": true,
"quotemark": [true, "single", "avoid-escape", "avoid-template"],
"radix": true,
"semicolon": [true, "always", "ignore-bound-class-methods"],
// "template-accessibility-alt-text": true,
// "template-accessibility-elements-content": true,
// "template-accessibility-label-for": true,
// "template-accessibility-tabindex-no-positive": true,
// "template-accessibility-table-scope": true,
// "template-accessibility-valid-aria": true,
// "template-banana-in-box": true,
// "template-click-events-have-key-events": true,
// "template-mouse-events-have-key-events": true,
// "template-no-autofocus": true,
// "template-no-distracting-elements": true,
// "template-no-negated-async": true,
"triple-equals": [true, "allow-null-check"],
"unified-signatures": true,
"use-lifecycle-interface": true,
"use-pipe-transform-interface": true
},
"rulesDirectory": ["codelyzer"]
"use-pipe-transform-interface": true,
"variable-name": false,
"prefer-for-of": false
}
}

Loading…
Cancel
Save