diff --git a/.browserslistrc b/.browserslistrc deleted file mode 100644 index 427441dc93..0000000000 --- a/.browserslistrc +++ /dev/null @@ -1,17 +0,0 @@ -# This file is used by the build system to adjust CSS and JS output to support the specified browsers below. -# For additional information regarding the format and rule options, please see: -# https://github.com/browserslist/browserslist#queries - -# For the full list of supported browsers by the Angular framework, please see: -# https://angular.io/guide/browser-support - -# You can see what browsers were selected by your queries by running: -# npx browserslist - -last 1 Chrome version -last 1 Firefox version -last 2 Edge major versions -last 2 Safari major versions -last 2 iOS major versions -Firefox ESR -not IE 11 # Angular supports IE 11 only as an opt-in. To opt-in, remove the 'not' prefix on this line. diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6fae54f1f9..219074780e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -31,6 +31,8 @@ jobs: # When Chrome version is specified, we pin to a specific version of Chrome # Comment this out to use the latest release #CHROME_VERSION: "90.0.4430.212-1" + # Bump Node heap size (OOM in CI after upgrading to Angular 15) + NODE_OPTIONS: '--max-old-space-size=4096' strategy: # Create a matrix of Node versions to test against (in parallel) matrix: diff --git a/angular.json b/angular.json index 56c75d7b2d..5e597d4d30 100644 --- a/angular.json +++ b/angular.json @@ -274,9 +274,18 @@ } } }, - "defaultProject": "dspace-angular", "cli": { "analytics": false, - "defaultCollection": "@angular-eslint/schematics" + "schematicCollections": [ + "@angular-eslint/schematics" + ] + }, + "schematics": { + "@angular-eslint/schematics:application": { + "setParserOptionsProject": true + }, + "@angular-eslint/schematics:library": { + "setParserOptionsProject": true + } } } diff --git a/config/config.example.yml b/config/config.example.yml index f1e6be76aa..ea38303fa3 100644 --- a/config/config.example.yml +++ b/config/config.example.yml @@ -187,6 +187,9 @@ languages: - code: gd label: Gàidhlig active: true + - code: it + label: Italiano + active: true - code: lv label: Latviešu active: true @@ -214,6 +217,9 @@ languages: - code: tr label: Türkçe active: true + - code: vi + label: Tiếng Việt + active: true - code: kk label: Қазақ active: true diff --git a/package.json b/package.json index e1bedeb02b..59766e993b 100644 --- a/package.json +++ b/package.json @@ -17,9 +17,9 @@ "build:stats": "ng build --stats-json", "build:prod": "yarn run build:ssr", "build:ssr": "ng build --configuration production && ng run dspace-angular:server:production", - "test": "ng test --sourceMap=true --watch=false --configuration test", - "test:watch": "nodemon --exec \"ng test --sourceMap=true --watch=true --configuration test\"", - "test:headless": "ng test --sourceMap=true --watch=false --configuration test --browsers=ChromeHeadless --code-coverage", + "test": "ng test --source-map=true --watch=false --configuration test", + "test:watch": "nodemon --exec \"ng test --source-map=true --watch=true --configuration test\"", + "test:headless": "ng test --source-map=true --watch=false --configuration test --browsers=ChromeHeadless --code-coverage", "lint": "ng lint", "lint-fix": "ng lint --fix=true", "e2e": "ng e2e", @@ -55,136 +55,136 @@ "ts-node": "10.2.1" }, "dependencies": { - "@angular/animations": "~13.3.12", - "@angular/cdk": "^13.2.6", - "@angular/common": "~13.3.12", - "@angular/compiler": "~13.3.12", - "@angular/core": "~13.3.12", - "@angular/forms": "~13.3.12", - "@angular/localize": "13.3.12", - "@angular/platform-browser": "~13.3.12", - "@angular/platform-browser-dynamic": "~13.3.12", - "@angular/platform-server": "~13.3.12", - "@angular/router": "~13.3.12", - "@babel/runtime": "7.17.2", + "@angular/animations": "^15.2.8", + "@angular/cdk": "^15.2.8", + "@angular/common": "^15.2.8", + "@angular/compiler": "^15.2.8", + "@angular/core": "^15.2.8", + "@angular/forms": "^15.2.8", + "@angular/localize": "15.2.8", + "@angular/platform-browser": "^15.2.8", + "@angular/platform-browser-dynamic": "^15.2.8", + "@angular/platform-server": "^15.2.8", + "@angular/router": "^15.2.8", + "@babel/runtime": "7.21.0", "@kolkov/ngx-gallery": "^2.0.1", "@material-ui/core": "^4.11.0", - "@material-ui/icons": "^4.9.1", + "@material-ui/icons": "^4.11.3", "@ng-bootstrap/ng-bootstrap": "^11.0.0", "@ng-dynamic-forms/core": "^15.0.0", "@ng-dynamic-forms/ui-ng-bootstrap": "^15.0.0", - "@ngrx/effects": "^13.0.2", - "@ngrx/router-store": "^13.0.2", - "@ngrx/store": "^13.0.2", - "@nguniversal/express-engine": "^13.0.2", - "@ngx-translate/core": "^13.0.0", - "@nicky-lenaers/ngx-scroll-to": "^13.0.0", + "@ngrx/effects": "^15.4.0", + "@ngrx/router-store": "^15.4.0", + "@ngrx/store": "^15.4.0", + "@nguniversal/express-engine": "^15.2.1", + "@ngx-translate/core": "^14.0.0", + "@nicky-lenaers/ngx-scroll-to": "^14.0.0", "@types/grecaptcha": "^3.0.4", "angular-idle-preload": "3.0.0", - "angulartics2": "^12.0.0", + "angulartics2": "^12.2.0", "axios": "^0.27.2", "bootstrap": "^4.6.1", "cerialize": "0.1.18", - "cli-progress": "^3.8.0", + "cli-progress": "^3.12.0", "colors": "^1.4.0", "compression": "^1.7.4", - "cookie-parser": "1.4.5", - "core-js": "^3.7.0", + "cookie-parser": "1.4.6", + "core-js": "^3.30.1", "date-fns": "^2.29.3", "date-fns-tz": "^1.3.7", - "deepmerge": "^4.2.2", - "ejs": "^3.1.8", - "express": "^4.17.1", + "deepmerge": "^4.3.1", + "ejs": "^3.1.9", + "express": "^4.18.2", "express-rate-limit": "^5.1.3", - "fast-json-patch": "^3.0.0-1", + "fast-json-patch": "^3.1.1", "filesize": "^6.1.0", "http-proxy-middleware": "^1.0.5", - "isbot": "^3.6.5", + "isbot": "^3.6.10", "js-cookie": "2.2.1", "js-yaml": "^4.1.0", - "json5": "^2.2.2", - "jsonschema": "1.4.0", + "json5": "^2.2.3", + "jsonschema": "1.4.1", "jwt-decode": "^3.1.2", "klaro": "^0.7.18", "lodash": "^4.17.21", "lru-cache": "^7.14.1", "markdown-it": "^13.0.1", - "markdown-it-mathjax3": "^4.3.1", + "markdown-it-mathjax3": "^4.3.2", "mirador": "^3.3.0", "mirador-dl-plugin": "^0.13.0", "mirador-share-plugin": "^0.11.0", "morgan": "^1.10.0", - "ng-mocks": "^13.1.1", + "ng-mocks": "^14.10.0", "ng2-file-upload": "1.4.0", "ng2-nouislider": "^1.8.3", - "ngx-infinite-scroll": "^10.0.1", - "ngx-pagination": "5.0.0", + "ngx-infinite-scroll": "^15.0.0", + "ngx-pagination": "6.0.3", "ngx-sortablejs": "^11.1.0", - "ngx-ui-switch": "^13.0.2", + "ngx-ui-switch": "^14.0.3", "nouislider": "^14.6.3", - "pem": "1.14.4", - "prop-types": "^15.7.2", - "react-copy-to-clipboard": "^5.0.1", + "pem": "1.14.7", + "prop-types": "^15.8.1", + "react-copy-to-clipboard": "^5.1.0", "reflect-metadata": "^0.1.13", - "rxjs": "^7.5.5", - "sanitize-html": "^2.7.2", - "sortablejs": "1.13.0", + "rxjs": "^7.8.0", + "sanitize-html": "^2.10.0", + "sortablejs": "1.15.0", "uuid": "^8.3.2", "webfontloader": "1.6.28", "zone.js": "~0.11.5" }, "devDependencies": { - "@angular-builders/custom-webpack": "~13.1.0", - "@angular-devkit/build-angular": "~13.3.10", - "@angular-eslint/builder": "13.1.0", - "@angular-eslint/eslint-plugin": "13.1.0", - "@angular-eslint/eslint-plugin-template": "13.1.0", - "@angular-eslint/schematics": "13.1.0", - "@angular-eslint/template-parser": "13.1.0", - "@angular/cli": "~13.3.10", - "@angular/compiler-cli": "~13.3.12", - "@angular/language-service": "~13.3.12", + "@angular-builders/custom-webpack": "~15.0.0", + "@angular-devkit/build-angular": "^15.2.6", + "@angular-eslint/builder": "15.2.1", + "@angular-eslint/eslint-plugin": "15.2.1", + "@angular-eslint/eslint-plugin-template": "15.2.1", + "@angular-eslint/schematics": "15.2.1", + "@angular-eslint/template-parser": "15.2.1", + "@angular/cli": "^15.2.6", + "@angular/compiler-cli": "^15.2.8", + "@angular/language-service": "^15.2.8", "@cypress/schematic": "^1.5.0", - "@fortawesome/fontawesome-free": "^6.2.1", - "@ngrx/store-devtools": "^13.0.2", - "@ngtools/webpack": "^13.2.6", - "@nguniversal/builders": "^13.1.1", + "@fortawesome/fontawesome-free": "^6.4.0", + "@ngrx/store-devtools": "^15.4.0", + "@ngtools/webpack": "^15.2.6", + "@nguniversal/builders": "^15.2.1", "@types/deep-freeze": "0.1.2", - "@types/ejs": "^3.1.1", - "@types/express": "^4.17.9", + "@types/ejs": "^3.1.2", + "@types/express": "^4.17.17", "@types/jasmine": "~3.6.0", "@types/js-cookie": "2.2.6", - "@types/lodash": "^4.14.165", + "@types/lodash": "^4.14.194", "@types/node": "^14.14.9", - "@types/sanitize-html": "^2.6.2", - "@typescript-eslint/eslint-plugin": "5.11.0", - "@typescript-eslint/parser": "5.11.0", - "axe-core": "^4.4.3", + "@types/sanitize-html": "^2.9.0", + "@typescript-eslint/eslint-plugin": "^5.59.1", + "@typescript-eslint/parser": "^5.59.1", + "axe-core": "^4.7.0", "compression-webpack-plugin": "^9.2.0", "copy-webpack-plugin": "^6.4.1", "cross-env": "^7.0.3", - "cypress": "12.9.0", - "cypress-axe": "^1.1.0", + "cypress": "12.10.0", + "cypress-axe": "^1.4.0", "deep-freeze": "0.0.1", - "eslint": "^8.2.0", - "eslint-plugin-deprecation": "^1.3.2", - "eslint-plugin-import": "^2.25.4", + "eslint": "^8.39.0", + "eslint-plugin-deprecation": "^1.4.1", + "eslint-plugin-import": "^2.27.5", "eslint-plugin-jsdoc": "^39.6.4", "eslint-plugin-jsonc": "^2.6.0", "eslint-plugin-lodash": "^7.4.0", "eslint-plugin-unused-imports": "^2.0.0", - "express-static-gzip": "^2.1.5", + "express-static-gzip": "^2.1.7", "jasmine-core": "^3.8.0", "jasmine-marbles": "0.9.2", - "karma": "^6.3.14", - "karma-chrome-launcher": "~3.1.0", - "karma-coverage-istanbul-reporter": "~3.0.2", + "karma": "^6.4.2", + "karma-chrome-launcher": "~3.2.0", + "karma-coverage-istanbul-reporter": "~3.0.3", "karma-jasmine": "~4.0.0", "karma-jasmine-html-reporter": "^1.5.0", "karma-mocha-reporter": "2.2.5", "ngx-mask": "^13.1.7", - "nodemon": "^2.0.20", - "postcss": "^8.1", + "nodemon": "^2.0.22", + "postcss": "^8.4", "postcss-apply": "0.12.0", "postcss-import": "^14.0.0", "postcss-loader": "^4.0.3", @@ -194,14 +194,14 @@ "react-dom": "^16.14.0", "rimraf": "^3.0.2", "rxjs-spy": "^8.0.2", - "sass": "~1.33.0", + "sass": "~1.62.0", "sass-loader": "^12.6.0", - "sass-resources-loader": "^2.1.1", + "sass-resources-loader": "^2.2.5", "ts-node": "^8.10.2", - "typescript": "~4.5.5", - "webpack": "^5.76.0", - "webpack-bundle-analyzer": "^4.4.0", + "typescript": "~4.8.4", + "webpack": "5.76.1", + "webpack-bundle-analyzer": "^4.8.0", "webpack-cli": "^4.2.0", - "webpack-dev-server": "^4.5.0" + "webpack-dev-server": "^4.13.3" } } diff --git a/src/app/access-control/epeople-registry/epeople-registry.component.ts b/src/app/access-control/epeople-registry/epeople-registry.component.ts index 55233d8173..eeeb10c7b3 100644 --- a/src/app/access-control/epeople-registry/epeople-registry.component.ts +++ b/src/app/access-control/epeople-registry/epeople-registry.component.ts @@ -1,5 +1,5 @@ import { Component, OnDestroy, OnInit } from '@angular/core'; -import { FormBuilder } from '@angular/forms'; +import { UntypedFormBuilder } from '@angular/forms'; import { Router } from '@angular/router'; import { TranslateService } from '@ngx-translate/core'; import { BehaviorSubject, combineLatest, Observable, Subscription } from 'rxjs'; @@ -89,7 +89,7 @@ export class EPeopleRegistryComponent implements OnInit, OnDestroy { private translateService: TranslateService, private notificationsService: NotificationsService, private authorizationService: AuthorizationDataService, - private formBuilder: FormBuilder, + private formBuilder: UntypedFormBuilder, private router: Router, private modalService: NgbModal, private paginationService: PaginationService, diff --git a/src/app/access-control/epeople-registry/eperson-form/eperson-form.component.spec.ts b/src/app/access-control/epeople-registry/eperson-form/eperson-form.component.spec.ts index bf03e1defb..fb911e709c 100644 --- a/src/app/access-control/epeople-registry/eperson-form/eperson-form.component.spec.ts +++ b/src/app/access-control/epeople-registry/eperson-form/eperson-form.component.spec.ts @@ -2,7 +2,7 @@ import { Observable, of as observableOf } from 'rxjs'; import { CommonModule } from '@angular/common'; import { NO_ERRORS_SCHEMA } from '@angular/core'; import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; -import { FormControl, FormGroup, FormsModule, ReactiveFormsModule, Validators } from '@angular/forms'; +import { UntypedFormControl, UntypedFormGroup, FormsModule, ReactiveFormsModule, Validators } from '@angular/forms'; import { BrowserModule, By } from '@angular/platform-browser'; import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; @@ -116,9 +116,9 @@ describe('EPersonFormComponent', () => { const controlModel = model; const controlState = { value: controlModel.value, disabled: controlModel.disabled }; const controlOptions = this.createAbstractControlOptions(controlModel.validators, controlModel.asyncValidators, controlModel.updateOn); - controls[model.id] = new FormControl(controlState, controlOptions); + controls[model.id] = new UntypedFormControl(controlState, controlOptions); }); - return new FormGroup(controls, options); + return new UntypedFormGroup(controls, options); }, createAbstractControlOptions(validatorsConfig = null, asyncValidatorsConfig = null, updateOn = null) { return { diff --git a/src/app/access-control/epeople-registry/eperson-form/eperson-form.component.ts b/src/app/access-control/epeople-registry/eperson-form/eperson-form.component.ts index 5a7ab735ca..8dbc9e92a4 100644 --- a/src/app/access-control/epeople-registry/eperson-form/eperson-form.component.ts +++ b/src/app/access-control/epeople-registry/eperson-form/eperson-form.component.ts @@ -1,5 +1,5 @@ import { ChangeDetectorRef, Component, EventEmitter, OnDestroy, OnInit, Output } from '@angular/core'; -import { FormGroup } from '@angular/forms'; +import { UntypedFormGroup } from '@angular/forms'; import { DynamicCheckboxModel, DynamicFormControlModel, @@ -108,7 +108,7 @@ export class EPersonFormComponent implements OnInit, OnDestroy { /** * A FormGroup that combines all inputs */ - formGroup: FormGroup; + formGroup: UntypedFormGroup; /** * An EventEmitter that's fired whenever the form is being submitted diff --git a/src/app/access-control/group-registry/group-form/group-form.component.spec.ts b/src/app/access-control/group-registry/group-form/group-form.component.spec.ts index a7a7cb5be4..d845550b55 100644 --- a/src/app/access-control/group-registry/group-form/group-form.component.spec.ts +++ b/src/app/access-control/group-registry/group-form/group-form.component.spec.ts @@ -2,7 +2,7 @@ import { CommonModule } from '@angular/common'; import { HttpClient } from '@angular/common/http'; import { NO_ERRORS_SCHEMA } from '@angular/core'; import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; -import { FormControl, FormGroup, FormsModule, ReactiveFormsModule, Validators } from '@angular/forms'; +import { UntypedFormControl, UntypedFormGroup, FormsModule, ReactiveFormsModule, Validators } from '@angular/forms'; import { BrowserModule, By } from '@angular/platform-browser'; import { ActivatedRoute, Router } from '@angular/router'; import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; @@ -130,9 +130,9 @@ describe('GroupFormComponent', () => { const controlModel = model; const controlState = { value: controlModel.value, disabled: controlModel.disabled }; const controlOptions = this.createAbstractControlOptions(controlModel.validators, controlModel.asyncValidators, controlModel.updateOn); - controls[model.id] = new FormControl(controlState, controlOptions); + controls[model.id] = new UntypedFormControl(controlState, controlOptions); }); - return new FormGroup(controls, options); + return new UntypedFormGroup(controls, options); }, createAbstractControlOptions(validatorsConfig = null, asyncValidatorsConfig = null, updateOn = null) { return { diff --git a/src/app/access-control/group-registry/group-form/group-form.component.ts b/src/app/access-control/group-registry/group-form/group-form.component.ts index 4302d126ea..72e34c1838 100644 --- a/src/app/access-control/group-registry/group-form/group-form.component.ts +++ b/src/app/access-control/group-registry/group-form/group-form.component.ts @@ -1,5 +1,5 @@ import { Component, EventEmitter, HostListener, OnDestroy, OnInit, Output, ChangeDetectorRef } from '@angular/core'; -import { FormGroup } from '@angular/forms'; +import { UntypedFormGroup } from '@angular/forms'; import { ActivatedRoute, Router } from '@angular/router'; import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; import { @@ -95,7 +95,7 @@ export class GroupFormComponent implements OnInit, OnDestroy { /** * A FormGroup that combines all inputs */ - formGroup: FormGroup; + formGroup: UntypedFormGroup; /** * An EventEmitter that's fired whenever the form is being submitted diff --git a/src/app/access-control/group-registry/group-form/members-list/members-list.component.ts b/src/app/access-control/group-registry/group-form/members-list/members-list.component.ts index d0fc046093..56ab2f1540 100644 --- a/src/app/access-control/group-registry/group-form/members-list/members-list.component.ts +++ b/src/app/access-control/group-registry/group-form/members-list/members-list.component.ts @@ -1,5 +1,5 @@ import { Component, Input, OnDestroy, OnInit } from '@angular/core'; -import { FormBuilder } from '@angular/forms'; +import { UntypedFormBuilder } from '@angular/forms'; import { Router } from '@angular/router'; import { TranslateService } from '@ngx-translate/core'; import { @@ -141,7 +141,7 @@ export class MembersListComponent implements OnInit, OnDestroy { public ePersonDataService: EPersonDataService, protected translateService: TranslateService, protected notificationsService: NotificationsService, - protected formBuilder: FormBuilder, + protected formBuilder: UntypedFormBuilder, protected paginationService: PaginationService, private router: Router ) { diff --git a/src/app/access-control/group-registry/group-form/subgroup-list/subgroups-list.component.ts b/src/app/access-control/group-registry/group-form/subgroup-list/subgroups-list.component.ts index 5f1700e07d..ba65026533 100644 --- a/src/app/access-control/group-registry/group-form/subgroup-list/subgroups-list.component.ts +++ b/src/app/access-control/group-registry/group-form/subgroup-list/subgroups-list.component.ts @@ -1,5 +1,5 @@ import { Component, Input, OnDestroy, OnInit } from '@angular/core'; -import { FormBuilder } from '@angular/forms'; +import { UntypedFormBuilder } from '@angular/forms'; import { Router } from '@angular/router'; import { TranslateService } from '@ngx-translate/core'; import { BehaviorSubject, Observable, of as observableOf, Subscription } from 'rxjs'; @@ -86,7 +86,7 @@ export class SubgroupsListComponent implements OnInit, OnDestroy { constructor(public groupDataService: GroupDataService, private translateService: TranslateService, private notificationsService: NotificationsService, - private formBuilder: FormBuilder, + private formBuilder: UntypedFormBuilder, private paginationService: PaginationService, private router: Router) { this.currentSearchQuery = ''; diff --git a/src/app/access-control/group-registry/groups-registry.component.ts b/src/app/access-control/group-registry/groups-registry.component.ts index 70c9b22852..e4c624e3f6 100644 --- a/src/app/access-control/group-registry/groups-registry.component.ts +++ b/src/app/access-control/group-registry/groups-registry.component.ts @@ -1,5 +1,5 @@ import { Component, OnDestroy, OnInit } from '@angular/core'; -import { FormBuilder } from '@angular/forms'; +import { UntypedFormBuilder } from '@angular/forms'; import { Router } from '@angular/router'; import { TranslateService } from '@ngx-translate/core'; import { @@ -99,7 +99,7 @@ export class GroupsRegistryComponent implements OnInit, OnDestroy { private dSpaceObjectDataService: DSpaceObjectDataService, private translateService: TranslateService, private notificationsService: NotificationsService, - private formBuilder: FormBuilder, + private formBuilder: UntypedFormBuilder, protected routeService: RouteService, private router: Router, private authorizationService: AuthorizationDataService, diff --git a/src/app/admin/admin-registries/metadata-registry/metadata-schema-form/metadata-schema-form.component.ts b/src/app/admin/admin-registries/metadata-registry/metadata-schema-form/metadata-schema-form.component.ts index 5c6885ae72..6d3138987a 100644 --- a/src/app/admin/admin-registries/metadata-registry/metadata-schema-form/metadata-schema-form.component.ts +++ b/src/app/admin/admin-registries/metadata-registry/metadata-schema-form/metadata-schema-form.component.ts @@ -5,7 +5,7 @@ import { DynamicFormLayout, DynamicInputModel } from '@ng-dynamic-forms/core'; -import { FormGroup } from '@angular/forms'; +import { UntypedFormGroup } from '@angular/forms'; import { RegistryService } from '../../../../core/registry/registry.service'; import { FormBuilderService } from '../../../../shared/form/builder/form-builder.service'; import { take } from 'rxjs/operators'; @@ -66,7 +66,7 @@ export class MetadataSchemaFormComponent implements OnInit, OnDestroy { /** * A FormGroup that combines all inputs */ - formGroup: FormGroup; + formGroup: UntypedFormGroup; /** * An EventEmitter that's fired whenever the form is being submitted diff --git a/src/app/admin/admin-registries/metadata-schema/metadata-field-form/metadata-field-form.component.ts b/src/app/admin/admin-registries/metadata-schema/metadata-field-form/metadata-field-form.component.ts index 1c000c3c76..0beb306d6c 100644 --- a/src/app/admin/admin-registries/metadata-schema/metadata-field-form/metadata-field-form.component.ts +++ b/src/app/admin/admin-registries/metadata-schema/metadata-field-form/metadata-field-form.component.ts @@ -5,7 +5,7 @@ import { DynamicFormLayout, DynamicInputModel } from '@ng-dynamic-forms/core'; -import { FormGroup } from '@angular/forms'; +import { UntypedFormGroup } from '@angular/forms'; import { RegistryService } from '../../../../core/registry/registry.service'; import { FormBuilderService } from '../../../../shared/form/builder/form-builder.service'; import { take } from 'rxjs/operators'; @@ -82,7 +82,7 @@ export class MetadataFieldFormComponent implements OnInit, OnDestroy { /** * A FormGroup that combines all inputs */ - formGroup: FormGroup; + formGroup: UntypedFormGroup; /** * An EventEmitter that's fired whenever the form is being submitted diff --git a/src/app/admin/admin-search-page/admin-search-results/admin-search-result-grid-element/item-search-result/item-admin-search-result-grid-element.component.spec.ts b/src/app/admin/admin-search-page/admin-search-results/admin-search-result-grid-element/item-search-result/item-admin-search-result-grid-element.component.spec.ts index e478aa3ef3..ee3de42131 100644 --- a/src/app/admin/admin-search-page/admin-search-results/admin-search-result-grid-element/item-search-result/item-admin-search-result-grid-element.component.spec.ts +++ b/src/app/admin/admin-search-page/admin-search-results/admin-search-result-grid-element/item-search-result/item-admin-search-result-grid-element.component.spec.ts @@ -19,7 +19,7 @@ import { createSuccessfulRemoteDataObject$ } from '../../../../../shared/remote- import { getMockThemeService } from '../../../../../shared/mocks/theme-service.mock'; import { ThemeService } from '../../../../../shared/theme-support/theme.service'; import { AccessStatusDataService } from '../../../../../core/data/access-status-data.service'; -import { AccessStatusObject } from '../../../../../shared/object-list/access-status-badge/access-status.model'; +import { AccessStatusObject } from '../../../../../shared/object-collection/shared/badges/access-status-badge/access-status.model'; import { AuthService } from '../../../../../core/auth/auth.service'; import { AuthServiceStub } from '../../../../../shared/testing/auth-service.stub'; import { FileService } from '../../../../../core/shared/file.service'; diff --git a/src/app/admin/admin-search-page/admin-search-results/admin-search-result-list-element/item-search-result/item-admin-search-result-list-element.component.html b/src/app/admin/admin-search-page/admin-search-results/admin-search-result-list-element/item-search-result/item-admin-search-result-list-element.component.html index 259512552c..991508335f 100644 --- a/src/app/admin/admin-search-page/admin-search-results/admin-search-result-list-element/item-search-result/item-admin-search-result-list-element.component.html +++ b/src/app/admin/admin-search-page/admin-search-results/admin-search-result-list-element/item-search-result/item-admin-search-result-list-element.component.html @@ -2,6 +2,5 @@ [viewMode]="viewModes.ListElement" [index]="index" [linkType]="linkType" - [listID]="listID" - [hideBadges]="true"> + [listID]="listID"> diff --git a/src/app/admin/admin-workflow-page/admin-workflow-search-results/actions/workspace-item/workspace-item-admin-workflow-actions.component.spec.ts b/src/app/admin/admin-workflow-page/admin-workflow-search-results/actions/workspace-item/workspace-item-admin-workflow-actions.component.spec.ts index 628fc3f89c..a8f0581ec0 100644 --- a/src/app/admin/admin-workflow-page/admin-workflow-search-results/actions/workspace-item/workspace-item-admin-workflow-actions.component.spec.ts +++ b/src/app/admin/admin-workflow-page/admin-workflow-search-results/actions/workspace-item/workspace-item-admin-workflow-actions.component.spec.ts @@ -11,7 +11,7 @@ import { URLCombiner } from '../../../../../core/url-combiner/url-combiner'; import { WorkspaceItemAdminWorkflowActionsComponent } from './workspace-item-admin-workflow-actions.component'; import { WorkspaceItem } from '../../../../../core/submission/models/workspaceitem.model'; import { - getWorkflowItemDeleteRoute, + getWorkspaceItemDeleteRoute, } from '../../../../../workflowitems-edit-page/workflowitems-edit-page-routing-paths'; import { Item } from '../../../../../core/shared/item.model'; import { RemoteData } from '../../../../../core/data/remote-data'; @@ -83,7 +83,7 @@ describe('WorkspaceItemAdminWorkflowActionsComponent', () => { it('should render a delete button with the correct link', () => { const button = fixture.debugElement.query(By.css('a.delete-link')); const link = button.nativeElement.href; - expect(link).toContain(new URLCombiner(getWorkflowItemDeleteRoute(wsi.id)).toString()); + expect(link).toContain(new URLCombiner(getWorkspaceItemDeleteRoute(wsi.id)).toString()); }); it('should render a policies button with the correct link', () => { diff --git a/src/app/admin/admin-workflow-page/admin-workflow-search-results/actions/workspace-item/workspace-item-admin-workflow-actions.component.ts b/src/app/admin/admin-workflow-page/admin-workflow-search-results/actions/workspace-item/workspace-item-admin-workflow-actions.component.ts index adbd421628..36678460da 100644 --- a/src/app/admin/admin-workflow-page/admin-workflow-search-results/actions/workspace-item/workspace-item-admin-workflow-actions.component.ts +++ b/src/app/admin/admin-workflow-page/admin-workflow-search-results/actions/workspace-item/workspace-item-admin-workflow-actions.component.ts @@ -11,7 +11,7 @@ import { SupervisionOrderGroupSelectorComponent } from './supervision-order-group-selector/supervision-order-group-selector.component'; import { - getWorkflowItemDeleteRoute + getWorkspaceItemDeleteRoute } from '../../../../../workflowitems-edit-page/workflowitems-edit-page-routing-paths'; import { ITEM_EDIT_AUTHORIZATIONS_PATH } from '../../../../../item-page/edit-item-page/edit-item-page.routing-paths'; import { WorkspaceItem } from '../../../../../core/submission/models/workspaceitem.model'; @@ -105,10 +105,10 @@ export class WorkspaceItemAdminWorkflowActionsComponent implements OnInit { } /** - * Returns the path to the delete page of this workflow item + * Returns the path to the delete page of this workspace item */ getDeleteRoute(): string { - return getWorkflowItemDeleteRoute(this.wsi.id); + return getWorkspaceItemDeleteRoute(this.wsi.id); } /** diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index b69e47c82c..deb68f1ea9 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -209,7 +209,7 @@ import { ThemedPageErrorComponent } from './page-error/themed-page-error.compone { path: REQUEST_COPY_MODULE_PATH, loadChildren: () => import('./request-copy/request-copy.module').then((m) => m.RequestCopyModule), - canActivate: [AuthenticatedGuard, EndUserAgreementCurrentUserGuard] + canActivate: [EndUserAgreementCurrentUserGuard] }, { path: FORBIDDEN_PATH, diff --git a/src/app/bitstream-page/edit-bitstream-page/edit-bitstream-page.component.html b/src/app/bitstream-page/edit-bitstream-page/edit-bitstream-page.component.html index 6454198340..26223e503d 100644 --- a/src/app/bitstream-page/edit-bitstream-page/edit-bitstream-page.component.html +++ b/src/app/bitstream-page/edit-bitstream-page/edit-bitstream-page.component.html @@ -2,7 +2,7 @@
- +
diff --git a/src/app/bitstream-page/edit-bitstream-page/edit-bitstream-page.component.spec.ts b/src/app/bitstream-page/edit-bitstream-page/edit-bitstream-page.component.spec.ts index 44e48182fd..6081a076c0 100644 --- a/src/app/bitstream-page/edit-bitstream-page/edit-bitstream-page.component.spec.ts +++ b/src/app/bitstream-page/edit-bitstream-page/edit-bitstream-page.component.spec.ts @@ -15,7 +15,7 @@ import { INotification, Notification } from '../../shared/notifications/models/n import { BitstreamFormat } from '../../core/shared/bitstream-format.model'; import { BitstreamFormatSupportLevel } from '../../core/shared/bitstream-format-support-level'; import { hasValue } from '../../shared/empty.util'; -import { FormControl, FormGroup } from '@angular/forms'; +import { UntypedFormControl, UntypedFormGroup } from '@angular/forms'; import { FileSizePipe } from '../../shared/utils/file-size-pipe'; import { VarDirective } from '../../shared/utils/var.directive'; import { createSuccessfulRemoteDataObject, createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils'; @@ -84,9 +84,9 @@ describe('EditBitstreamPageComponent', () => { const controls = {}; if (hasValue(fModel)) { fModel.forEach((controlModel) => { - controls[controlModel.id] = new FormControl((controlModel as any).value); + controls[controlModel.id] = new UntypedFormControl((controlModel as any).value); }); - return new FormGroup(controls); + return new UntypedFormGroup(controls); } return undefined; } diff --git a/src/app/bitstream-page/edit-bitstream-page/edit-bitstream-page.component.ts b/src/app/bitstream-page/edit-bitstream-page/edit-bitstream-page.component.ts index 8e63ec939f..1a3048097e 100644 --- a/src/app/bitstream-page/edit-bitstream-page/edit-bitstream-page.component.ts +++ b/src/app/bitstream-page/edit-bitstream-page/edit-bitstream-page.component.ts @@ -23,7 +23,7 @@ import { DynamicInputModel, DynamicSelectModel } from '@ng-dynamic-forms/core'; -import { FormGroup } from '@angular/forms'; +import { UntypedFormGroup } from '@angular/forms'; import { TranslateService } from '@ngx-translate/core'; import { DynamicCustomSwitchModel } from '../../shared/form/builder/ds-dynamic-form-ui/models/custom-switch/custom-switch.model'; import cloneDeep from 'lodash/cloneDeep'; @@ -359,7 +359,7 @@ export class EditBitstreamPageComponent implements OnInit, OnDestroy { /** * The form group of this form */ - formGroup: FormGroup; + formGroup: UntypedFormGroup; /** * The ID of the item the bitstream originates from diff --git a/src/app/collection-page/edit-collection-page/collection-source/collection-source.component.spec.ts b/src/app/collection-page/edit-collection-page/collection-source/collection-source.component.spec.ts index fbaac87ed6..e7e98d9523 100644 --- a/src/app/collection-page/edit-collection-page/collection-source/collection-source.component.spec.ts +++ b/src/app/collection-page/edit-collection-page/collection-source/collection-source.component.spec.ts @@ -12,7 +12,7 @@ import { NotificationType } from '../../../shared/notifications/models/notificat import { NotificationsService } from '../../../shared/notifications/notifications.service'; import { DynamicFormControlModel, DynamicFormService } from '@ng-dynamic-forms/core'; import { hasValue } from '../../../shared/empty.util'; -import { FormControl, FormGroup } from '@angular/forms'; +import { UntypedFormControl, UntypedFormGroup } from '@angular/forms'; import { RouterStub } from '../../../shared/testing/router.stub'; import { By } from '@angular/platform-browser'; import { Collection } from '../../../core/shared/collection.model'; @@ -98,9 +98,9 @@ describe('CollectionSourceComponent', () => { const controls = {}; if (hasValue(fModel)) { fModel.forEach((controlModel) => { - controls[controlModel.id] = new FormControl((controlModel as any).value); + controls[controlModel.id] = new UntypedFormControl((controlModel as any).value); }); - return new FormGroup(controls); + return new UntypedFormGroup(controls); } return undefined; } diff --git a/src/app/collection-page/edit-collection-page/collection-source/collection-source.component.ts b/src/app/collection-page/edit-collection-page/collection-source/collection-source.component.ts index 512faa5311..2d1308cc83 100644 --- a/src/app/collection-page/edit-collection-page/collection-source/collection-source.component.ts +++ b/src/app/collection-page/edit-collection-page/collection-source/collection-source.component.ts @@ -14,7 +14,7 @@ import { Location } from '@angular/common'; import { TranslateService } from '@ngx-translate/core'; import { ObjectUpdatesService } from '../../../core/data/object-updates/object-updates.service'; import { NotificationsService } from '../../../shared/notifications/notifications.service'; -import { FormGroup } from '@angular/forms'; +import { UntypedFormGroup } from '@angular/forms'; import { hasNoValue, hasValue, isNotEmpty } from '../../../shared/empty.util'; import { ContentSource, ContentSourceHarvestType } from '../../../core/shared/content-source.model'; import { Observable, Subscription } from 'rxjs'; @@ -202,7 +202,7 @@ export class CollectionSourceComponent extends AbstractTrackableComponent implem /** * The form group of this form */ - formGroup: FormGroup; + formGroup: UntypedFormGroup; /** * Subscription to update the current form diff --git a/src/app/community-list-page/community-list/community-list.component.html b/src/app/community-list-page/community-list/community-list.component.html index ea772bb891..ddda4df9fa 100644 --- a/src/app/community-list-page/community-list/community-list.component.html +++ b/src/app/community-list-page/community-list/community-list.component.html @@ -8,10 +8,10 @@
diff --git a/src/app/community-list-page/community-list/community-list.component.spec.ts b/src/app/community-list-page/community-list/community-list.component.spec.ts index 575edf14e8..ce6b27dbeb 100644 --- a/src/app/community-list-page/community-list/community-list.component.spec.ts +++ b/src/app/community-list-page/community-list/community-list.component.spec.ts @@ -16,6 +16,7 @@ import { of as observableOf } from 'rxjs'; import { By } from '@angular/platform-browser'; import { isEmpty, isNotEmpty } from '../../shared/empty.util'; import { FlatNode } from '../flat-node.model'; +import { RouterLinkWithHref } from '@angular/router'; describe('CommunityListComponent', () => { let component: CommunityListComponent; @@ -193,7 +194,8 @@ describe('CommunityListComponent', () => { }, }), CdkTreeModule, - RouterTestingModule], + RouterTestingModule, + RouterLinkWithHref], declarations: [CommunityListComponent], providers: [CommunityListComponent, { provide: CommunityListService, useValue: communityListServiceStub },], @@ -230,9 +232,14 @@ describe('CommunityListComponent', () => { expect(showMoreEl).toBeTruthy(); }); + it('should not render the show more button as an empty link', () => { + const debugElements = fixture.debugElement.queryAll(By.directive(RouterLinkWithHref)); + expect(debugElements).toBeTruthy(); + }); + describe('when show more of top communities is clicked', () => { beforeEach(fakeAsync(() => { - const showMoreLink = fixture.debugElement.query(By.css('.show-more-node a')); + const showMoreLink = fixture.debugElement.query(By.css('.show-more-node .btn-outline-primary')); showMoreLink.triggerEventHandler('click', { preventDefault: () => {/**/ } @@ -240,6 +247,7 @@ describe('CommunityListComponent', () => { tick(); fixture.detectChanges(); })); + it('tree contains maximum of currentPage (2) * (2) elementsPerPage of first top communities, or less if there are less communities (3)', () => { const expandableNodesFound = fixture.debugElement.queryAll(By.css('.expandable-node a')); const childlessNodesFound = fixture.debugElement.queryAll(By.css('.childless-node a')); diff --git a/src/app/community-page/sub-collection-list/community-page-sub-collection-list.component.ts b/src/app/community-page/sub-collection-list/community-page-sub-collection-list.component.ts index 181ee7cd6f..3a77149e5b 100644 --- a/src/app/community-page/sub-collection-list/community-page-sub-collection-list.component.ts +++ b/src/app/community-page/sub-collection-list/community-page-sub-collection-list.component.ts @@ -1,4 +1,5 @@ import { Component, Input, OnDestroy, OnInit } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; import { BehaviorSubject, combineLatest as observableCombineLatest } from 'rxjs'; @@ -49,19 +50,23 @@ export class CommunityPageSubCollectionListComponent implements OnInit, OnDestro */ subCollectionsRDObs: BehaviorSubject>> = new BehaviorSubject>>({} as any); - constructor(private cds: CollectionDataService, - private paginationService: PaginationService, - - ) {} + constructor( + protected cds: CollectionDataService, + protected paginationService: PaginationService, + protected route: ActivatedRoute, + ) { + } ngOnInit(): void { this.config = new PaginationComponentOptions(); this.config.id = this.pageId; if (hasValue(this.pageSize)) { this.config.pageSize = this.pageSize; + } else { + this.config.pageSize = this.route.snapshot.queryParams[this.pageId + '.rpp'] ?? this.config.pageSize; } - this.config.currentPage = 1; - this.sortConfig = new SortOptions('dc.title', SortDirection.ASC); + this.config.currentPage = this.route.snapshot.queryParams[this.pageId + '.page'] ?? 1; + this.sortConfig = new SortOptions('dc.title', SortDirection[this.route.snapshot.queryParams[this.pageId + '.sd']] ?? SortDirection.ASC); this.initPage(); } diff --git a/src/app/community-page/sub-community-list/community-page-sub-community-list.component.ts b/src/app/community-page/sub-community-list/community-page-sub-community-list.component.ts index e9da10bfa1..5a0409a051 100644 --- a/src/app/community-page/sub-community-list/community-page-sub-community-list.component.ts +++ b/src/app/community-page/sub-community-list/community-page-sub-community-list.component.ts @@ -1,4 +1,5 @@ import { Component, Input, OnDestroy, OnInit } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; import { BehaviorSubject, combineLatest as observableCombineLatest } from 'rxjs'; @@ -9,7 +10,6 @@ import { PaginatedList } from '../../core/data/paginated-list.model'; import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model'; import { SortDirection, SortOptions } from '../../core/cache/models/sort-options.model'; import { CommunityDataService } from '../../core/data/community-data.service'; -import { takeUntilCompletedRemoteData } from '../../core/shared/operators'; import { switchMap } from 'rxjs/operators'; import { PaginationService } from '../../core/pagination/pagination.service'; import { hasValue } from '../../shared/empty.util'; @@ -52,8 +52,10 @@ export class CommunityPageSubCommunityListComponent implements OnInit, OnDestroy */ subCommunitiesRDObs: BehaviorSubject>> = new BehaviorSubject>>({} as any); - constructor(private cds: CommunityDataService, - private paginationService: PaginationService + constructor( + protected cds: CommunityDataService, + protected paginationService: PaginationService, + protected route: ActivatedRoute, ) { } @@ -62,9 +64,11 @@ export class CommunityPageSubCommunityListComponent implements OnInit, OnDestroy this.config.id = this.pageId; if (hasValue(this.pageSize)) { this.config.pageSize = this.pageSize; + } else { + this.config.pageSize = this.route.snapshot.queryParams[this.pageId + '.rpp'] ?? this.config.pageSize; } - this.config.currentPage = 1; - this.sortConfig = new SortOptions('dc.title', SortDirection.ASC); + this.config.currentPage = this.route.snapshot.queryParams[this.pageId + '.page'] ?? 1; + this.sortConfig = new SortOptions('dc.title', SortDirection[this.route.snapshot.queryParams[this.pageId + '.sd']] ?? SortDirection.ASC); this.initPage(); } @@ -86,15 +90,6 @@ export class CommunityPageSubCommunityListComponent implements OnInit, OnDestroy ).subscribe((results) => { this.subCommunitiesRDObs.next(results); }); - - - this.cds.findByParent(this.community.id, { - currentPage: this.config.currentPage, - elementsPerPage: this.config.pageSize, - sort: { field: this.sortConfig.field, direction: this.sortConfig.direction } - }).pipe(takeUntilCompletedRemoteData()).subscribe((results) => { - this.subCommunitiesRDObs.next(results); - }); } ngOnDestroy(): void { diff --git a/src/app/core/cache/builders/link.service.ts b/src/app/core/cache/builders/link.service.ts index b7af54abf7..afc7ab88e4 100644 --- a/src/app/core/cache/builders/link.service.ts +++ b/src/app/core/cache/builders/link.service.ts @@ -60,7 +60,7 @@ export class LinkService { const provider = this.getDataServiceFor(matchingLinkDef.resourceType); if (hasNoValue(provider)) { - throw new Error(`The @link() for ${linkToFollow.name} on ${model.constructor.name} models uses the resource type ${matchingLinkDef.resourceType.value.toUpperCase()}, but there is no service with an @dataService(${matchingLinkDef.resourceType.value.toUpperCase()}) annotation in order to retrieve it`); + throw new Error(`The @link() for ${String(linkToFollow.name)} on ${model.constructor.name} models uses the resource type ${matchingLinkDef.resourceType.value.toUpperCase()}, but there is no service with an @dataService(${matchingLinkDef.resourceType.value.toUpperCase()}) annotation in order to retrieve it`); } const service: HALDataService = Injector.create({ @@ -79,12 +79,12 @@ export class LinkService { return service.findByHref(href, linkToFollow.useCachedVersionIfAvailable, linkToFollow.reRequestOnStale, ...linkToFollow.linksToFollow); } } catch (e) { - console.error(`Something went wrong when using @dataService(${matchingLinkDef.resourceType.value}) ${hasValue(service) ? '' : '(undefined) '}to resolve link ${linkToFollow.name} at ${href}`); + console.error(`Something went wrong when using @dataService(${matchingLinkDef.resourceType.value}) ${hasValue(service) ? '' : '(undefined) '}to resolve link ${String(linkToFollow.name)} at ${href}`); throw e; } } } else if (!linkToFollow.isOptional) { - throw new Error(`followLink('${linkToFollow.name}') was used as a required link for a ${model.constructor.name}, but there is no property on ${model.constructor.name} models with an @link() for ${linkToFollow.name}`); + throw new Error(`followLink('${String(linkToFollow.name)}') was used as a required link for a ${model.constructor.name}, but there is no property on ${model.constructor.name} models with an @link() for ${String(linkToFollow.name)}`); } return EMPTY; diff --git a/src/app/core/cache/builders/remote-data-build.service.ts b/src/app/core/cache/builders/remote-data-build.service.ts index 76529891d4..075bf3ca0c 100644 --- a/src/app/core/cache/builders/remote-data-build.service.ts +++ b/src/app/core/cache/builders/remote-data-build.service.ts @@ -73,7 +73,7 @@ export class RemoteDataBuildService { if (getResourceTypeValueFor((obj as any).type) === PAGINATED_LIST.value) { return this.buildPaginatedList(obj, ...linksToFollow); } else if (isNotEmpty(linksToFollow)) { - return [this.linkService.resolveLinks(obj, ...linksToFollow)]; + return [this.linkService.resolveLinks(obj as any, ...linksToFollow)]; } } return [obj]; diff --git a/src/app/core/core.module.ts b/src/app/core/core.module.ts index 319b42d58b..d35900ebe2 100644 --- a/src/app/core/core.module.ts +++ b/src/app/core/core.module.ts @@ -160,7 +160,7 @@ import { SubmissionAccessesModel } from './config/models/config-submission-acces import { RatingAdvancedWorkflowInfo } from './tasks/models/rating-advanced-workflow-info.model'; import { AdvancedWorkflowInfo } from './tasks/models/advanced-workflow-info.model'; import { SelectReviewerAdvancedWorkflowInfo } from './tasks/models/select-reviewer-advanced-workflow-info.model'; -import { AccessStatusObject } from '../shared/object-list/access-status-badge/access-status.model'; +import { AccessStatusObject } from '../shared/object-collection/shared/badges/access-status-badge/access-status.model'; import { AccessStatusDataService } from './data/access-status-data.service'; import { LinkHeadService } from './services/link-head.service'; import { ResearcherProfileDataService } from './profile/researcher-profile-data.service'; diff --git a/src/app/core/data/access-status-data.service.ts b/src/app/core/data/access-status-data.service.ts index 2f641456fa..e8b77245e8 100644 --- a/src/app/core/data/access-status-data.service.ts +++ b/src/app/core/data/access-status-data.service.ts @@ -3,8 +3,8 @@ import { RemoteDataBuildService } from '../cache/builders/remote-data-build.serv import { ObjectCacheService } from '../cache/object-cache.service'; import { HALEndpointService } from '../shared/hal-endpoint.service'; import { RequestService } from './request.service'; -import { AccessStatusObject } from 'src/app/shared/object-list/access-status-badge/access-status.model'; -import { ACCESS_STATUS } from 'src/app/shared/object-list/access-status-badge/access-status.resource-type'; +import { AccessStatusObject } from 'src/app/shared/object-collection/shared/badges/access-status-badge/access-status.model'; +import { ACCESS_STATUS } from 'src/app/shared/object-collection/shared/badges/access-status-badge/access-status.resource-type'; import { Observable } from 'rxjs'; import { RemoteData } from './remote-data'; import { Item } from '../shared/item.model'; diff --git a/src/app/core/data/base/base-data.service.spec.ts b/src/app/core/data/base/base-data.service.spec.ts index 17532f477a..098f075c10 100644 --- a/src/app/core/data/base/base-data.service.spec.ts +++ b/src/app/core/data/base/base-data.service.spec.ts @@ -641,6 +641,62 @@ describe('BaseDataService', () => { }); }); + describe('hasCachedResponse', () => { + it('should return false when the request will be dispatched', (done) => { + const result = service.hasCachedResponse('test-href'); + + result.subscribe((hasCachedResponse) => { + expect(hasCachedResponse).toBeFalse(); + done(); + }); + }); + + it('should return true when the request will not be dispatched', (done) => { + (requestService.shouldDispatchRequest as jasmine.Spy).and.returnValue(false); + const result = service.hasCachedResponse('test-href'); + + result.subscribe((hasCachedResponse) => { + expect(hasCachedResponse).toBeTrue(); + done(); + }); + }); + }); + + describe('hasCachedErrorResponse', () => { + it('should return false when no response is cached', (done) => { + spyOn(service,'hasCachedResponse').and.returnValue(observableOf(false)); + const result = service.hasCachedErrorResponse('test-href'); + + result.subscribe((hasCachedErrorResponse) => { + expect(hasCachedErrorResponse).toBeFalse(); + done(); + }); + }); + it('should return false when no error response is cached', (done) => { + spyOn(service,'hasCachedResponse').and.returnValue(observableOf(true)); + spyOn(rdbService,'buildSingle').and.returnValue(createSuccessfulRemoteDataObject$({})); + + const result = service.hasCachedErrorResponse('test-href'); + + result.subscribe((hasCachedErrorResponse) => { + expect(hasCachedErrorResponse).toBeFalse(); + done(); + }); + }); + + it('should return true when an error response is cached', (done) => { + spyOn(service,'hasCachedResponse').and.returnValue(observableOf(true)); + spyOn(rdbService,'buildSingle').and.returnValue(createFailedRemoteDataObject$()); + + const result = service.hasCachedErrorResponse('test-href'); + + result.subscribe((hasCachedErrorResponse) => { + expect(hasCachedErrorResponse).toBeTrue(); + done(); + }); + }); + }); + describe('addDependency', () => { let addDependencySpy; diff --git a/src/app/core/data/base/base-data.service.ts b/src/app/core/data/base/base-data.service.ts index 85603580a4..edd6d9e2a4 100644 --- a/src/app/core/data/base/base-data.service.ts +++ b/src/app/core/data/base/base-data.service.ts @@ -341,6 +341,48 @@ export class BaseDataService implements HALDataServic } } + /** + * Checks for the provided href whether a response is already cached + * @param href$ The url for which to check whether there is a cached response. + * Can be a string or an Observable + */ + hasCachedResponse(href$: string | Observable): Observable { + if (isNotEmpty(href$)) { + if (typeof href$ === 'string') { + href$ = observableOf(href$); + } + return href$.pipe( + isNotEmptyOperator(), + take(1), + map((href: string) => { + const requestId = this.requestService.generateRequestId(); + const request = new GetRequest(requestId, href); + return !this.requestService.shouldDispatchRequest(request, true); + }), + ); + } + throw new Error(`Can't check whether there is a cached response for an empty href$`); + } + + /** + * Checks for the provided href whether an ERROR response is currently cached + * @param href$ The url for which to check whether there is a cached ERROR response. + * Can be a string or an Observable + */ + hasCachedErrorResponse(href$: string | Observable): Observable { + return this.hasCachedResponse(href$).pipe( + switchMap((hasCachedResponse) => { + if (hasCachedResponse) { + return this.rdbService.buildSingle(href$).pipe( + getFirstCompletedRemoteData(), + map((rd => rd.hasFailed)) + ); + } + return observableOf(false); + }) + ); + } + /** * Return the links to traverse from the root of the api to the * endpoint this DataService represents diff --git a/src/app/core/data/external-source-data.service.spec.ts b/src/app/core/data/external-source-data.service.spec.ts index cdbdbaa006..723d7f9bed 100644 --- a/src/app/core/data/external-source-data.service.spec.ts +++ b/src/app/core/data/external-source-data.service.spec.ts @@ -5,6 +5,7 @@ import { ExternalSourceEntry } from '../shared/external-source-entry.model'; import { of as observableOf } from 'rxjs'; import { GetRequest } from './request.models'; import { testSearchDataImplementation } from './base/search-data.spec'; +import { take } from 'rxjs/operators'; describe('ExternalSourceService', () => { let service: ExternalSourceDataService; @@ -64,19 +65,42 @@ describe('ExternalSourceService', () => { }); describe('getExternalSourceEntries', () => { - let result; - beforeEach(() => { - result = service.getExternalSourceEntries('test'); + describe('when no error response is cached', () => { + let result; + beforeEach(() => { + spyOn(service, 'hasCachedErrorResponse').and.returnValue(observableOf(false)); + result = service.getExternalSourceEntries('test'); + }); + + it('should send a GetRequest', () => { + result.pipe(take(1)).subscribe(); + expect(requestService.send).toHaveBeenCalledWith(jasmine.any(GetRequest), true); + }); + + it('should return the entries', () => { + result.subscribe((resultRD) => { + expect(resultRD.payload.page).toBe(entries); + }); + }); }); - it('should send a GetRequest', () => { - expect(requestService.send).toHaveBeenCalledWith(jasmine.any(GetRequest), true); - }); + describe('when an error response is cached', () => { + let result; + beforeEach(() => { + spyOn(service, 'hasCachedErrorResponse').and.returnValue(observableOf(true)); + result = service.getExternalSourceEntries('test'); + }); - it('should return the entries', () => { - result.subscribe((resultRD) => { - expect(resultRD.payload.page).toBe(entries); + it('should send a GetRequest', () => { + result.pipe(take(1)).subscribe(); + expect(requestService.send).toHaveBeenCalledWith(jasmine.any(GetRequest), false); + }); + + it('should return the entries', () => { + result.subscribe((resultRD) => { + expect(resultRD.payload.page).toBe(entries); + }); }); }); }); diff --git a/src/app/core/data/external-source-data.service.ts b/src/app/core/data/external-source-data.service.ts index c0552aeaec..02c5e4a53c 100644 --- a/src/app/core/data/external-source-data.service.ts +++ b/src/app/core/data/external-source-data.service.ts @@ -74,7 +74,12 @@ export class ExternalSourceDataService extends IdentifiableDataService { + return this.findListByHref(href$, undefined, !hasCachedErrorResponse, reRequestOnStale, ...linksToFollow as any); + }) + ) as any; } /** diff --git a/src/app/core/locale/locale.service.spec.ts b/src/app/core/locale/locale.service.spec.ts index e6a4de0327..39356fdf97 100644 --- a/src/app/core/locale/locale.service.spec.ts +++ b/src/app/core/locale/locale.service.spec.ts @@ -28,7 +28,7 @@ describe('LocaleService test suite', () => { isAuthenticationLoaded: jasmine.createSpy('isAuthenticationLoaded') }); - const langList = ['en', 'it', 'de']; + const langList = ['en', 'xx', 'de']; beforeEach(waitForAsync(() => { return TestBed.configureTestingModule({ @@ -82,8 +82,8 @@ describe('LocaleService test suite', () => { }); it('should return language from browser setting', () => { - spyOn(translateService, 'getBrowserLang').and.returnValue('it'); - expect(service.getCurrentLanguageCode()).toBe('it'); + spyOn(translateService, 'getBrowserLang').and.returnValue('xx'); + expect(service.getCurrentLanguageCode()).toBe('xx'); }); it('should return default language from config', () => { @@ -114,9 +114,9 @@ describe('LocaleService test suite', () => { }); it('should set the given language', () => { - service.setCurrentLanguageCode('it'); - expect(translateService.use).toHaveBeenCalledWith('it'); - expect(service.saveLanguageCodeToCookie).toHaveBeenCalledWith('it'); + service.setCurrentLanguageCode('xx'); + expect(translateService.use).toHaveBeenCalledWith('xx'); + expect(service.saveLanguageCodeToCookie).toHaveBeenCalledWith('xx'); }); it('should set the current language', () => { @@ -135,7 +135,7 @@ describe('LocaleService test suite', () => { describe('', () => { it('should set quality to current language list', () => { - const langListWithQuality = ['en;q=1', 'it;q=0.9', 'de;q=0.8']; + const langListWithQuality = ['en;q=1', 'xx;q=0.9', 'de;q=0.8']; spyOn(service, 'setQuality').and.returnValue(langListWithQuality); service.setQuality(langList, LANG_ORIGIN.BROWSER, false); expect(service.setQuality).toHaveBeenCalledWith(langList, LANG_ORIGIN.BROWSER, false); diff --git a/src/app/core/shared/context.model.ts b/src/app/core/shared/context.model.ts index dbe5a64552..756fe5c673 100644 --- a/src/app/core/shared/context.model.ts +++ b/src/app/core/shared/context.model.ts @@ -3,17 +3,38 @@ */ export enum Context { + /** Default context */ Any = 'undefined', + + /** General item page context */ ItemPage = 'itemPage', + + /** General search page context */ Search = 'search', + Workflow = 'workflow', Workspace = 'workspace', SupervisedItems = 'supervisedWorkspace', + + /** Administrative menu context */ AdminMenu = 'adminMenu', + EntitySearchModalWithNameVariants = 'EntitySearchModalWithNameVariants', EntitySearchModal = 'EntitySearchModal', + + /** Administrative search page context */ AdminSearch = 'adminSearch', AdminWorkflowSearch = 'adminWorkflowSearch', + SideBarSearchModal = 'sideBarSearchModal', SideBarSearchModalCurrent = 'sideBarSearchModalCurrent', + + /** The MyDSpace* Context values below are used for badge display in MyDSpace. */ + MyDSpaceArchived = 'mydspaceArchived', + MyDSpaceWorkspace = 'mydspaceWorkspace', + MyDSpaceWorkflow = 'mydspaceWorkflow', + MyDSpaceDeclined = 'mydspaceDeclined', + MyDSpaceApproved = 'mydspaceApproved', + MyDSpaceWaitingController = 'mydspaceWaitingController', + MyDSpaceValidation = 'mydspaceValidation', } diff --git a/src/app/core/shared/item.model.ts b/src/app/core/shared/item.model.ts index 3441896ed1..20fc275ee2 100644 --- a/src/app/core/shared/item.model.ts +++ b/src/app/core/shared/item.model.ts @@ -21,8 +21,8 @@ import { Version } from './version.model'; import { VERSION } from './version.resource-type'; import { BITSTREAM } from './bitstream.resource-type'; import { Bitstream } from './bitstream.model'; -import { ACCESS_STATUS } from 'src/app/shared/object-list/access-status-badge/access-status.resource-type'; -import { AccessStatusObject } from 'src/app/shared/object-list/access-status-badge/access-status.model'; +import { ACCESS_STATUS } from 'src/app/shared/object-collection/shared/badges/access-status-badge/access-status.resource-type'; +import { AccessStatusObject } from 'src/app/shared/object-collection/shared/badges/access-status-badge/access-status.model'; import { HandleObject } from './handle-object.model'; import { IDENTIFIERS } from '../../shared/object-list/identifier-data/identifier-data.resource-type'; import { IdentifierData } from '../../shared/object-list/identifier-data/identifier-data.model'; diff --git a/src/app/curation-form/curation-form.component.ts b/src/app/curation-form/curation-form.component.ts index 4b67580e77..2c0e900a66 100644 --- a/src/app/curation-form/curation-form.component.ts +++ b/src/app/curation-form/curation-form.component.ts @@ -1,6 +1,6 @@ -import { Component, Input, OnInit } from '@angular/core'; +import { ChangeDetectorRef, Component, Input, OnInit } from '@angular/core'; import { ScriptDataService } from '../core/data/processes/script-data.service'; -import { FormControl, FormGroup } from '@angular/forms'; +import { UntypedFormControl, UntypedFormGroup } from '@angular/forms'; import { getFirstCompletedRemoteData } from '../core/shared/operators'; import { find, map } from 'rxjs/operators'; import { NotificationsService } from '../shared/notifications/notifications.service'; @@ -28,7 +28,7 @@ export class CurationFormComponent implements OnInit { config: Observable>; tasks: string[]; - form: FormGroup; + form: UntypedFormGroup; @Input() dsoHandle: string; @@ -40,14 +40,15 @@ export class CurationFormComponent implements OnInit { private notificationsService: NotificationsService, private translateService: TranslateService, private handleService: HandleService, - private router: Router + private router: Router, + private cdr: ChangeDetectorRef ) { } ngOnInit(): void { - this.form = new FormGroup({ - task: new FormControl(''), - handle: new FormControl('') + this.form = new UntypedFormGroup({ + task: new UntypedFormControl(''), + handle: new UntypedFormControl('') }); this.config = this.configurationDataService.findByPropertyName(CURATION_CFG); @@ -59,6 +60,7 @@ export class CurationFormComponent implements OnInit { .filter((value) => isNotEmpty(value) && value.includes('=')) .map((value) => value.split('=')[1].trim()); this.form.get('task').patchValue(this.tasks[0]); + this.cdr.detectChanges(); }); } diff --git a/src/app/dso-shared/dso-edit-metadata/dso-edit-metadata-value/dso-edit-metadata-value.component.html b/src/app/dso-shared/dso-edit-metadata/dso-edit-metadata-value/dso-edit-metadata-value.component.html index f54dde4971..525b42610b 100644 --- a/src/app/dso-shared/dso-edit-metadata/dso-edit-metadata-value/dso-edit-metadata-value.component.html +++ b/src/app/dso-shared/dso-edit-metadata/dso-edit-metadata-value/dso-edit-metadata-value.component.html @@ -7,7 +7,7 @@ [dsDebounce]="300" (onDebounce)="confirm.emit(false)">
diff --git a/src/app/dso-shared/dso-edit-metadata/dso-edit-metadata-value/dso-edit-metadata-value.component.spec.ts b/src/app/dso-shared/dso-edit-metadata/dso-edit-metadata-value/dso-edit-metadata-value.component.spec.ts index 10b3016a52..67a6f98ac0 100644 --- a/src/app/dso-shared/dso-edit-metadata/dso-edit-metadata-value/dso-edit-metadata-value.component.spec.ts +++ b/src/app/dso-shared/dso-edit-metadata/dso-edit-metadata-value/dso-edit-metadata-value.component.spec.ts @@ -68,7 +68,7 @@ describe('DsoEditMetadataValueComponent', () => { }); it('should not show a badge', () => { - expect(fixture.debugElement.query(By.css('ds-type-badge'))).toBeNull(); + expect(fixture.debugElement.query(By.css('ds-themed-type-badge'))).toBeNull(); }); describe('when no changes have been made', () => { @@ -134,7 +134,7 @@ describe('DsoEditMetadataValueComponent', () => { }); it('should show a badge', () => { - expect(fixture.debugElement.query(By.css('ds-type-badge'))).toBeTruthy(); + expect(fixture.debugElement.query(By.css('ds-themed-type-badge'))).toBeTruthy(); }); assertButton(EDIT_BTN, true, true); diff --git a/src/app/dso-shared/dso-edit-metadata/metadata-field-selector/metadata-field-selector.component.ts b/src/app/dso-shared/dso-edit-metadata/metadata-field-selector/metadata-field-selector.component.ts index 5053a4b83d..5f76d87265 100644 --- a/src/app/dso-shared/dso-edit-metadata/metadata-field-selector/metadata-field-selector.component.ts +++ b/src/app/dso-shared/dso-edit-metadata/metadata-field-selector/metadata-field-selector.component.ts @@ -17,7 +17,7 @@ import { } from '../../../core/shared/operators'; import { Observable } from 'rxjs/internal/Observable'; import { RegistryService } from '../../../core/registry/registry.service'; -import { FormControl } from '@angular/forms'; +import { UntypedFormControl } from '@angular/forms'; import { BehaviorSubject } from 'rxjs/internal/BehaviorSubject'; import { hasValue } from '../../../shared/empty.util'; import { Subscription } from 'rxjs/internal/Subscription'; @@ -70,7 +70,7 @@ export class MetadataFieldSelectorComponent implements OnInit, OnDestroy, AfterV /** * FormControl for the input */ - public input: FormControl = new FormControl(); + public input: UntypedFormControl = new UntypedFormControl(); /** * The current query to update mdFieldOptions$ for diff --git a/src/app/entity-groups/journal-entities/item-grid-elements/search-result-grid-elements/journal-issue/journal-issue-search-result-grid-element.component.html b/src/app/entity-groups/journal-entities/item-grid-elements/search-result-grid-elements/journal-issue/journal-issue-search-result-grid-element.component.html index c6c29c2f1a..c6a2f5bcc3 100644 --- a/src/app/entity-groups/journal-entities/item-grid-elements/search-result-grid-elements/journal-issue/journal-issue-search-result-grid-element.component.html +++ b/src/app/entity-groups/journal-entities/item-grid-elements/search-result-grid-elements/journal-issue/journal-issue-search-result-grid-element.component.html @@ -8,18 +8,18 @@ rel="noopener noreferrer" [routerLink]="[itemPageRoute]" class="card-img-top full-width" [attr.title]="'search.results.view-result' | translate">
- - + +
- - + +
- +

diff --git a/src/app/entity-groups/journal-entities/item-grid-elements/search-result-grid-elements/journal-volume/journal-volume-search-result-grid-element.component.html b/src/app/entity-groups/journal-entities/item-grid-elements/search-result-grid-elements/journal-volume/journal-volume-search-result-grid-element.component.html index 5849105f96..b7e1330c86 100644 --- a/src/app/entity-groups/journal-entities/item-grid-elements/search-result-grid-elements/journal-volume/journal-volume-search-result-grid-element.component.html +++ b/src/app/entity-groups/journal-entities/item-grid-elements/search-result-grid-elements/journal-volume/journal-volume-search-result-grid-element.component.html @@ -8,18 +8,18 @@ rel="noopener noreferrer" [routerLink]="[itemPageRoute]" class="card-img-top full-width" [attr.title]="'search.results.view-result' | translate">
- - + +
- - + +
- +

diff --git a/src/app/entity-groups/journal-entities/item-grid-elements/search-result-grid-elements/journal/journal-search-result-grid-element.component.html b/src/app/entity-groups/journal-entities/item-grid-elements/search-result-grid-elements/journal/journal-search-result-grid-element.component.html index 9a3ea95c07..e4c9ada3ca 100644 --- a/src/app/entity-groups/journal-entities/item-grid-elements/search-result-grid-elements/journal/journal-search-result-grid-element.component.html +++ b/src/app/entity-groups/journal-entities/item-grid-elements/search-result-grid-elements/journal/journal-search-result-grid-element.component.html @@ -8,18 +8,18 @@ rel="noopener noreferrer" [routerLink]="[itemPageRoute]" class="card-img-top full-width" [attr.title]="'search.results.view-result' | translate">
- - + +
- - + +
- +

diff --git a/src/app/entity-groups/journal-entities/item-list-elements/search-result-list-elements/journal-issue/journal-issue-search-result-list-element.component.html b/src/app/entity-groups/journal-entities/item-list-elements/search-result-list-elements/journal-issue/journal-issue-search-result-list-element.component.html index 36b7e98c51..9d02a5d837 100644 --- a/src/app/entity-groups/journal-entities/item-list-elements/search-result-list-elements/journal-issue/journal-issue-search-result-list-element.component.html +++ b/src/app/entity-groups/journal-entities/item-list-elements/search-result-list-elements/journal-issue/journal-issue-search-result-list-element.component.html @@ -12,7 +12,7 @@
- +
- +
- +
- +
- +
- - + +
- - + +
- +

diff --git a/src/app/entity-groups/research-entities/item-grid-elements/search-result-grid-elements/person/person-search-result-grid-element.component.html b/src/app/entity-groups/research-entities/item-grid-elements/search-result-grid-elements/person/person-search-result-grid-element.component.html index 853a717965..fe26fd7063 100644 --- a/src/app/entity-groups/research-entities/item-grid-elements/search-result-grid-elements/person/person-search-result-grid-element.component.html +++ b/src/app/entity-groups/research-entities/item-grid-elements/search-result-grid-elements/person/person-search-result-grid-element.component.html @@ -8,18 +8,18 @@ rel="noopener noreferrer" [routerLink]="[itemPageRoute]" class="card-img-top full-width" [attr.title]="'search.results.view-result' | translate">
- - + +
- - + +
- +

diff --git a/src/app/entity-groups/research-entities/item-grid-elements/search-result-grid-elements/project/project-search-result-grid-element.component.html b/src/app/entity-groups/research-entities/item-grid-elements/search-result-grid-elements/project/project-search-result-grid-element.component.html index a54d136de2..cf47f947cc 100644 --- a/src/app/entity-groups/research-entities/item-grid-elements/search-result-grid-elements/project/project-search-result-grid-element.component.html +++ b/src/app/entity-groups/research-entities/item-grid-elements/search-result-grid-elements/project/project-search-result-grid-element.component.html @@ -8,18 +8,18 @@ rel="noopener noreferrer" [routerLink]="[itemPageRoute]" class="card-img-top full-width" [attr.title]="'search.results.view-result' | translate">
- - + +
- - + +
- +

diff --git a/src/app/entity-groups/research-entities/item-list-elements/search-result-list-elements/org-unit/org-unit-search-result-list-element.component.html b/src/app/entity-groups/research-entities/item-list-elements/search-result-list-elements/org-unit/org-unit-search-result-list-element.component.html index 0bba83a209..9495577c01 100644 --- a/src/app/entity-groups/research-entities/item-list-elements/search-result-list-elements/org-unit/org-unit-search-result-list-element.component.html +++ b/src/app/entity-groups/research-entities/item-list-elements/search-result-list-elements/org-unit/org-unit-search-result-list-element.component.html @@ -18,7 +18,7 @@
- +
- +
- +
- - +
- - +
- - + diff --git a/src/app/entity-groups/research-entities/metadata-representations/org-unit/org-unit-item-metadata-list-element.component.html b/src/app/entity-groups/research-entities/metadata-representations/org-unit/org-unit-item-metadata-list-element.component.html index 1771f3d2bc..ec4dbd4323 100644 --- a/src/app/entity-groups/research-entities/metadata-representations/org-unit/org-unit-item-metadata-list-element.component.html +++ b/src/app/entity-groups/research-entities/metadata-representations/org-unit/org-unit-item-metadata-list-element.component.html @@ -1,12 +1,12 @@ - + - + + [innerHTML]="mdRepresentation.getValue()" + [ngbTooltip]="mdRepresentation.allMetadata(['dc.description']).length > 0 ? descTemplate : null"> diff --git a/src/app/entity-groups/research-entities/metadata-representations/org-unit/org-unit-item-metadata-list-element.component.spec.ts b/src/app/entity-groups/research-entities/metadata-representations/org-unit/org-unit-item-metadata-list-element.component.spec.ts index eff6fd0b31..429f2986b9 100644 --- a/src/app/entity-groups/research-entities/metadata-representations/org-unit/org-unit-item-metadata-list-element.component.spec.ts +++ b/src/app/entity-groups/research-entities/metadata-representations/org-unit/org-unit-item-metadata-list-element.component.spec.ts @@ -34,7 +34,7 @@ describe('OrgUnitItemMetadataListElementComponent', () => { beforeEach(() => { fixture = TestBed.createComponent(OrgUnitItemMetadataListElementComponent); comp = fixture.componentInstance; - comp.metadataRepresentation = mockItemMetadataRepresentation; + comp.mdRepresentation = mockItemMetadataRepresentation; fixture.detectChanges(); }); diff --git a/src/app/entity-groups/research-entities/metadata-representations/person/person-item-metadata-list-element.component.html b/src/app/entity-groups/research-entities/metadata-representations/person/person-item-metadata-list-element.component.html index 97632117f4..6f56056781 100644 --- a/src/app/entity-groups/research-entities/metadata-representations/person/person-item-metadata-list-element.component.html +++ b/src/app/entity-groups/research-entities/metadata-representations/person/person-item-metadata-list-element.component.html @@ -1,15 +1,15 @@ - - + - + + [innerHTML]="mdRepresentation.getValue()" + [ngbTooltip]="mdRepresentation.allMetadata(['person.jobTitle']).length > 0 ? descTemplate : null"> diff --git a/src/app/entity-groups/research-entities/metadata-representations/person/person-item-metadata-list-element.component.spec.ts b/src/app/entity-groups/research-entities/metadata-representations/person/person-item-metadata-list-element.component.spec.ts index 895cf52223..b9ebf19b67 100644 --- a/src/app/entity-groups/research-entities/metadata-representations/person/person-item-metadata-list-element.component.spec.ts +++ b/src/app/entity-groups/research-entities/metadata-representations/person/person-item-metadata-list-element.component.spec.ts @@ -36,7 +36,7 @@ describe('PersonItemMetadataListElementComponent', () => { beforeEach(() => { fixture = TestBed.createComponent(PersonItemMetadataListElementComponent); comp = fixture.componentInstance; - comp.metadataRepresentation = mockItemMetadataRepresentation; + comp.mdRepresentation = mockItemMetadataRepresentation; fixture.detectChanges(); }); diff --git a/src/app/entity-groups/research-entities/submission/item-list-elements/org-unit/org-unit-search-result-list-submission-element.component.html b/src/app/entity-groups/research-entities/submission/item-list-elements/org-unit/org-unit-search-result-list-submission-element.component.html index 6123dc3dc8..e264958738 100644 --- a/src/app/entity-groups/research-entities/submission/item-list-elements/org-unit/org-unit-search-result-list-submission-element.component.html +++ b/src/app/entity-groups/research-entities/submission/item-list-elements/org-unit/org-unit-search-result-list-submission-element.component.html @@ -1,6 +1,6 @@
- +
{ {provide: ActivatedRoute, useValue: route}, {provide: Store, useValue: store}, {provide: EPersonDataService, useValue: ePersonDataService}, - {provide: FormBuilder, useValue: new FormBuilder()}, + {provide: UntypedFormBuilder, useValue: new UntypedFormBuilder()}, {provide: NotificationsService, useValue: notificationsService}, ], schemas: [CUSTOM_ELEMENTS_SCHEMA] diff --git a/src/app/header-nav-wrapper/header-navbar-wrapper.component.scss b/src/app/header-nav-wrapper/header-navbar-wrapper.component.scss index b297979fd0..b02b3c378c 100644 --- a/src/app/header-nav-wrapper/header-navbar-wrapper.component.scss +++ b/src/app/header-nav-wrapper/header-navbar-wrapper.component.scss @@ -1,7 +1,4 @@ -@media screen and (max-width: map-get($grid-breakpoints, md)) { - :host.open { - background-color: var(--bs-white); - top: 0; - position: sticky; - } +:host { + position: relative; + z-index: var(--ds-nav-z-index); } diff --git a/src/app/header-nav-wrapper/themed-header-navbar-wrapper.component.scss b/src/app/header-nav-wrapper/themed-header-navbar-wrapper.component.scss deleted file mode 100644 index db392096aa..0000000000 --- a/src/app/header-nav-wrapper/themed-header-navbar-wrapper.component.scss +++ /dev/null @@ -1,3 +0,0 @@ -:host { - z-index: var(--ds-nav-z-index); -} diff --git a/src/app/header/header.component.scss b/src/app/header/header.component.scss index 546f6a06fa..cca3ed2abb 100644 --- a/src/app/header/header.component.scss +++ b/src/app/header/header.component.scss @@ -11,13 +11,12 @@ line-height: 1.5; } -.navbar ::ng-deep { - a { - color: var(--ds-header-icon-color); +.navbar-toggler { + border: none; + color: var(--ds-header-icon-color); - &:hover, &:focus { - color: var(--ds-header-icon-color-hover); - } + &:hover, &:focus { + color: var(--ds-header-icon-color-hover); } } diff --git a/src/app/home-page/home-page.component.html b/src/app/home-page/home-page.component.html index 87613b56d0..caa86ac290 100644 --- a/src/app/home-page/home-page.component.html +++ b/src/app/home-page/home-page.component.html @@ -4,6 +4,6 @@ - +
diff --git a/src/app/home-page/home-page.module.ts b/src/app/home-page/home-page.module.ts index 3418437d3c..1681abd805 100644 --- a/src/app/home-page/home-page.module.ts +++ b/src/app/home-page/home-page.module.ts @@ -12,11 +12,13 @@ import { ThemedHomePageComponent } from './themed-home-page.component'; import { RecentItemListComponent } from './recent-item-list/recent-item-list.component'; import { JournalEntitiesModule } from '../entity-groups/journal-entities/journal-entities.module'; import { ResearchEntitiesModule } from '../entity-groups/research-entities/research-entities.module'; +import { ThemedTopLevelCommunityListComponent } from './top-level-community-list/themed-top-level-community-list.component'; const DECLARATIONS = [ HomePageComponent, ThemedHomePageComponent, TopLevelCommunityListComponent, + ThemedTopLevelCommunityListComponent, ThemedHomeNewsComponent, HomeNewsComponent, RecentItemListComponent diff --git a/src/app/home-page/top-level-community-list/themed-top-level-community-list.component.ts b/src/app/home-page/top-level-community-list/themed-top-level-community-list.component.ts new file mode 100644 index 0000000000..6eb74cc0a9 --- /dev/null +++ b/src/app/home-page/top-level-community-list/themed-top-level-community-list.component.ts @@ -0,0 +1,25 @@ +import { Component } from '@angular/core'; +import { TopLevelCommunityListComponent } from './top-level-community-list.component'; +import { ThemedComponent } from '../../shared/theme-support/themed.component'; + +@Component({ + selector: 'ds-themed-top-level-community-list', + styleUrls: [], + templateUrl: '../../shared/theme-support/themed.component.html', +}) +export class ThemedTopLevelCommunityListComponent extends ThemedComponent { + protected inAndOutputNames: (keyof TopLevelCommunityListComponent & keyof this)[]; + + protected getComponentName(): string { + return 'TopLevelCommunityListComponent'; + } + + protected importThemedComponent(themeName: string): Promise { + return import(`../../../themes/${themeName}/app/home-page/top-level-community-list/top-level-community-list.component`); + } + + protected importUnthemedComponent(): Promise { + return import(`./top-level-community-list.component`); + } + +} diff --git a/src/app/info/feedback/feedback-form/feedback-form.component.spec.ts b/src/app/info/feedback/feedback-form/feedback-form.component.spec.ts index d6bedc46cf..c3d38a2876 100644 --- a/src/app/info/feedback/feedback-form/feedback-form.component.spec.ts +++ b/src/app/info/feedback/feedback-form/feedback-form.component.spec.ts @@ -7,7 +7,7 @@ import { DebugElement, NO_ERRORS_SCHEMA } from '@angular/core'; import { By } from '@angular/platform-browser'; import { RouteService } from '../../../core/services/route.service'; import { routeServiceStub } from '../../../shared/testing/route-service.stub'; -import { FormBuilder } from '@angular/forms'; +import { UntypedFormBuilder } from '@angular/forms'; import { NotificationsService } from '../../../shared/notifications/notifications.service'; import { NotificationsServiceStub } from '../../../shared/testing/notifications-service.stub'; import { AuthService } from '../../../core/auth/auth.service'; @@ -41,7 +41,7 @@ describe('FeedbackFormComponent', () => { declarations: [FeedbackFormComponent], providers: [ { provide: RouteService, useValue: routeServiceStub }, - { provide: FormBuilder, useValue: new FormBuilder() }, + { provide: UntypedFormBuilder, useValue: new UntypedFormBuilder() }, { provide: NotificationsService, useValue: notificationService }, { provide: FeedbackDataService, useValue: feedbackDataServiceStub }, { provide: AuthService, useValue: authService }, diff --git a/src/app/info/feedback/feedback-form/feedback-form.component.ts b/src/app/info/feedback/feedback-form/feedback-form.component.ts index 35cdd69cfc..684f974701 100644 --- a/src/app/info/feedback/feedback-form/feedback-form.component.ts +++ b/src/app/info/feedback/feedback-form/feedback-form.component.ts @@ -3,7 +3,7 @@ import { NoContent } from '../../../core/shared/NoContent.model'; import { FeedbackDataService } from '../../../core/feedback/feedback-data.service'; import { Component, Inject, OnInit } from '@angular/core'; import { RouteService } from '../../../core/services/route.service'; -import { FormBuilder, Validators } from '@angular/forms'; +import { UntypedFormBuilder, Validators } from '@angular/forms'; import { NotificationsService } from '../../../shared/notifications/notifications.service'; import { TranslateService } from '@ngx-translate/core'; import { AuthService } from '../../../core/auth/auth.service'; @@ -37,7 +37,7 @@ export class FeedbackFormComponent implements OnInit { constructor( @Inject(NativeWindowService) protected _window: NativeWindowRef, public routeService: RouteService, - private fb: FormBuilder, + private fb: UntypedFormBuilder, protected notificationsService: NotificationsService, protected translate: TranslateService, private feedbackDataService: FeedbackDataService, diff --git a/src/app/item-page/bitstreams/request-a-copy/bitstream-request-a-copy-page.component.ts b/src/app/item-page/bitstreams/request-a-copy/bitstream-request-a-copy-page.component.ts index 59819a4a66..77e1049d87 100644 --- a/src/app/item-page/bitstreams/request-a-copy/bitstream-request-a-copy-page.component.ts +++ b/src/app/item-page/bitstreams/request-a-copy/bitstream-request-a-copy-page.component.ts @@ -11,7 +11,7 @@ import { combineLatest as observableCombineLatest, Observable, of as observableO import { getBitstreamDownloadRoute, getForbiddenRoute } from '../../../app-routing-paths'; import { TranslateService } from '@ngx-translate/core'; import { EPerson } from '../../../core/eperson/models/eperson.model'; -import { FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms'; +import { UntypedFormBuilder, UntypedFormControl, UntypedFormGroup, Validators } from '@angular/forms'; import { ItemRequestDataService } from '../../../core/data/item-request-data.service'; import { ItemRequest } from '../../../core/shared/item-request.model'; import { Item } from '../../../core/shared/item.model'; @@ -34,7 +34,7 @@ export class BitstreamRequestACopyPageComponent implements OnInit, OnDestroy { canDownload$: Observable; private subs: Subscription[] = []; - requestCopyForm: FormGroup; + requestCopyForm: UntypedFormGroup; item: Item; itemName: string; @@ -49,7 +49,7 @@ export class BitstreamRequestACopyPageComponent implements OnInit, OnDestroy { protected router: Router, private authorizationService: AuthorizationDataService, private auth: AuthService, - private formBuilder: FormBuilder, + private formBuilder: UntypedFormBuilder, private itemRequestDataService: ItemRequestDataService, private notificationsService: NotificationsService, private dsoNameService: DSONameService, @@ -59,15 +59,15 @@ export class BitstreamRequestACopyPageComponent implements OnInit, OnDestroy { ngOnInit(): void { this.requestCopyForm = this.formBuilder.group({ - name: new FormControl('', { + name: new UntypedFormControl('', { validators: [Validators.required], }), - email: new FormControl('', { + email: new UntypedFormControl('', { validators: [Validators.required, Validators.pattern('^[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,4}$')] }), - allfiles: new FormControl(''), - message: new FormControl(''), + allfiles: new UntypedFormControl(''), + message: new UntypedFormControl(''), }); diff --git a/src/app/item-page/edit-item-page/edit-item-page.module.ts b/src/app/item-page/edit-item-page/edit-item-page.module.ts index 24d27b3340..7692df8924 100644 --- a/src/app/item-page/edit-item-page/edit-item-page.module.ts +++ b/src/app/item-page/edit-item-page/edit-item-page.module.ts @@ -38,7 +38,7 @@ import { IdentifierDataService } from '../../core/data/identifier-data.service'; import { IdentifierDataComponent } from '../../shared/object-list/identifier-data/identifier-data.component'; import { ItemRegisterDoiComponent } from './item-register-doi/item-register-doi.component'; import { DsoSharedModule } from '../../dso-shared/dso-shared.module'; - +import { ItemCurateComponent } from './item-curate/item-curate.component'; /** * Module that contains all components related to the Edit Item page administrator functionality @@ -81,7 +81,8 @@ import { DsoSharedModule } from '../../dso-shared/dso-shared.module'; VirtualMetadataComponent, ItemAuthorizationsComponent, IdentifierDataComponent, - ItemRegisterDoiComponent + ItemRegisterDoiComponent, + ItemCurateComponent ], providers: [ BundleDataService, diff --git a/src/app/item-page/edit-item-page/edit-item-page.routing.module.ts b/src/app/item-page/edit-item-page/edit-item-page.routing.module.ts index 88172e2620..9923748338 100644 --- a/src/app/item-page/edit-item-page/edit-item-page.routing.module.ts +++ b/src/app/item-page/edit-item-page/edit-item-page.routing.module.ts @@ -41,6 +41,7 @@ import { ItemPageVersionHistoryGuard } from './item-page-version-history.guard'; import { ItemPageCollectionMapperGuard } from './item-page-collection-mapper.guard'; import { ThemedDsoEditMetadataComponent } from '../../dso-shared/dso-edit-metadata/themed-dso-edit-metadata.component'; import { ItemPageRegisterDoiGuard } from './item-page-register-doi.guard'; +import { ItemCurateComponent } from './item-curate/item-curate.component'; /** * Routing module that handles the routing for the Edit Item page administrator functionality @@ -82,6 +83,11 @@ import { ItemPageRegisterDoiGuard } from './item-page-register-doi.guard'; data: { title: 'item.edit.tabs.metadata.title', showBreadcrumbs: true }, canActivate: [ItemPageMetadataGuard] }, + { + path: 'curate', + component: ItemCurateComponent, + data: { title: 'item.edit.tabs.curate.title', showBreadcrumbs: true } + }, { path: 'relationships', component: ItemRelationshipsComponent, diff --git a/src/app/item-page/edit-item-page/item-curate/item-curate.component.html b/src/app/item-page/edit-item-page/item-curate/item-curate.component.html new file mode 100644 index 0000000000..7c7ed41bd9 --- /dev/null +++ b/src/app/item-page/edit-item-page/item-curate/item-curate.component.html @@ -0,0 +1,7 @@ +
+

{{'item.edit.curate.title' |translate:{item: (itemName$ |async)} }}

+ +
diff --git a/src/app/item-page/edit-item-page/item-curate/item-curate.component.spec.ts b/src/app/item-page/edit-item-page/item-curate/item-curate.component.spec.ts new file mode 100644 index 0000000000..c104b4400b --- /dev/null +++ b/src/app/item-page/edit-item-page/item-curate/item-curate.component.spec.ts @@ -0,0 +1,75 @@ +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; +import { TranslateModule } from '@ngx-translate/core'; +import { CUSTOM_ELEMENTS_SCHEMA, DebugElement } from '@angular/core'; +import { ItemCurateComponent } from './item-curate.component'; +import { of as observableOf } from 'rxjs'; +import { createSuccessfulRemoteDataObject } from '../../../shared/remote-data.utils'; +import { ActivatedRoute } from '@angular/router'; +import { DSONameService } from '../../../core/breadcrumbs/dso-name.service'; +import { Item } from '../../../core/shared/item.model'; + +describe('ItemCurateComponent', () => { + let comp: ItemCurateComponent; + let fixture: ComponentFixture; + let debugEl: DebugElement; + + let routeStub; + let dsoNameService; + + const item = Object.assign(new Item(), { + handle: '123456789/1', + metadata: {'dc.title': ['Item Name']} + }); + + beforeEach(waitForAsync(() => { + routeStub = { + parent: { + data: observableOf({ + dso: createSuccessfulRemoteDataObject(item) + }) + } + }; + + dsoNameService = jasmine.createSpyObj('dsoNameService', { + getName: 'Item Name' + }); + + TestBed.configureTestingModule({ + imports: [TranslateModule.forRoot()], + declarations: [ItemCurateComponent], + providers: [ + {provide: ActivatedRoute, useValue: routeStub}, + {provide: DSONameService, useValue: dsoNameService} + ], + schemas: [CUSTOM_ELEMENTS_SCHEMA] + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ItemCurateComponent); + comp = fixture.componentInstance; + debugEl = fixture.debugElement; + + fixture.detectChanges(); + }); + describe('init', () => { + it('should initialise the comp', () => { + expect(comp).toBeDefined(); + expect(debugEl.nativeElement.innerHTML).toContain('ds-curation-form'); + }); + + it('should contain the item information provided in the route', (done) => { + comp.dsoRD$.subscribe((value) => { + expect(value.payload.handle).toEqual('123456789/1'); + done(); + }); + }); + + it('should contain the item name', (done) => { + comp.itemName$.subscribe((value) => { + expect(value).toEqual('Item Name'); + done(); + }); + }); + }); +}); diff --git a/src/app/item-page/edit-item-page/item-curate/item-curate.component.ts b/src/app/item-page/edit-item-page/item-curate/item-curate.component.ts new file mode 100644 index 0000000000..fa1e0287fa --- /dev/null +++ b/src/app/item-page/edit-item-page/item-curate/item-curate.component.ts @@ -0,0 +1,39 @@ +import { Component, OnInit } from '@angular/core'; +import { filter, map, take } from 'rxjs/operators'; +import { RemoteData } from '../../../core/data/remote-data'; +import { Observable } from 'rxjs'; +import { ActivatedRoute } from '@angular/router'; +import { DSONameService } from '../../../core/breadcrumbs/dso-name.service'; +import { hasValue } from '../../../shared/empty.util'; +import { Item } from '../../../core/shared/item.model'; + +/** + * Component for managing a collection's curation tasks + */ +@Component({ + selector: 'ds-item-curate', + templateUrl: './item-curate.component.html', +}) +export class ItemCurateComponent implements OnInit { + dsoRD$: Observable>; + itemName$: Observable; + + constructor( + private route: ActivatedRoute, + private dsoNameService: DSONameService, + ) {} + + ngOnInit(): void { + this.dsoRD$ = this.route.parent.data.pipe( + take(1), + map((data) => data.dso), + ); + + this.itemName$ = this.dsoRD$.pipe( + filter((rd: RemoteData) => hasValue(rd)), + map((rd: RemoteData) => { + return this.dsoNameService.getName(rd.payload); + }) + ); + } +} diff --git a/src/app/item-page/full/field-components/file-section/full-file-section.component.html b/src/app/item-page/full/field-components/file-section/full-file-section.component.html index f474c666f2..98fb191e1e 100644 --- a/src/app/item-page/full/field-components/file-section/full-file-section.component.html +++ b/src/app/item-page/full/field-components/file-section/full-file-section.component.html @@ -13,7 +13,7 @@
- +
@@ -55,7 +55,7 @@
- +
diff --git a/src/app/item-page/orcid-page/orcid-sync-settings/orcid-sync-settings.component.spec.ts b/src/app/item-page/orcid-page/orcid-sync-settings/orcid-sync-settings.component.spec.ts index cd466ae4a4..f2fa9d2440 100644 --- a/src/app/item-page/orcid-page/orcid-sync-settings/orcid-sync-settings.component.spec.ts +++ b/src/app/item-page/orcid-page/orcid-sync-settings/orcid-sync-settings.component.spec.ts @@ -1,6 +1,6 @@ import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; -import { FormControl, FormGroup, FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { UntypedFormControl, UntypedFormGroup, FormsModule, ReactiveFormsModule } from '@angular/forms'; import { RouterTestingModule } from '@angular/router/testing'; import { By } from '@angular/platform-browser'; @@ -26,7 +26,7 @@ describe('OrcidSyncSettingsComponent test suite', () => { let scheduler: TestScheduler; let researcherProfileService: jasmine.SpyObj; let notificationsService; - let formGroup: FormGroup; + let formGroup: UntypedFormGroup; const mockResearcherProfile: ResearcherProfile = Object.assign(new ResearcherProfile(), { id: 'test-id', @@ -186,12 +186,12 @@ describe('OrcidSyncSettingsComponent test suite', () => { beforeEach(() => { scheduler = getTestScheduler(); notificationsService = (comp as any).notificationsService; - formGroup = new FormGroup({ - syncMode: new FormControl('MANUAL'), - syncFundings: new FormControl('ALL'), - syncPublications: new FormControl('ALL'), - syncProfile_BIOGRAPHICAL: new FormControl(true), - syncProfile_IDENTIFIERS: new FormControl(true), + formGroup = new UntypedFormGroup({ + syncMode: new UntypedFormControl('MANUAL'), + syncFundings: new UntypedFormControl('ALL'), + syncPublications: new UntypedFormControl('ALL'), + syncProfile_BIOGRAPHICAL: new UntypedFormControl(true), + syncProfile_IDENTIFIERS: new UntypedFormControl(true), }); spyOn(comp.settingsUpdated, 'emit'); }); diff --git a/src/app/item-page/orcid-page/orcid-sync-settings/orcid-sync-settings.component.ts b/src/app/item-page/orcid-page/orcid-sync-settings/orcid-sync-settings.component.ts index 494075c0f0..0bcbc295ac 100644 --- a/src/app/item-page/orcid-page/orcid-sync-settings/orcid-sync-settings.component.ts +++ b/src/app/item-page/orcid-page/orcid-sync-settings/orcid-sync-settings.component.ts @@ -1,5 +1,5 @@ import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; -import { FormGroup } from '@angular/forms'; +import { UntypedFormGroup } from '@angular/forms'; import { TranslateService } from '@ngx-translate/core'; import { Operation } from 'fast-json-patch'; @@ -127,7 +127,7 @@ export class OrcidSyncSettingsComponent implements OnInit { * * @param form The form group */ - onSubmit(form: FormGroup): void { + onSubmit(form: UntypedFormGroup): void { const operations: Operation[] = []; this.fillOperationsFor(operations, '/orcid/mode', form.value.syncMode); this.fillOperationsFor(operations, '/orcid/publications', form.value.syncPublications); diff --git a/src/app/item-page/simple/field-components/file-section/file-section.component.html b/src/app/item-page/simple/field-components/file-section/file-section.component.html index 8e4f4dfcb0..82d934fe7b 100644 --- a/src/app/item-page/simple/field-components/file-section/file-section.component.html +++ b/src/app/item-page/simple/field-components/file-section/file-section.component.html @@ -8,10 +8,10 @@
- {{'item.page.bitstreams.view-more' | translate}} +
- {{'item.page.bitstreams.collapse' | translate}} +
diff --git a/src/app/item-page/simple/item-types/publication/publication.component.html b/src/app/item-page/simple/item-types/publication/publication.component.html index dcf243c63b..3749f63964 100644 --- a/src/app/item-page/simple/item-types/publication/publication.component.html +++ b/src/app/item-page/simple/item-types/publication/publication.component.html @@ -17,7 +17,7 @@
- +
diff --git a/src/app/item-page/simple/item-types/untyped-item/untyped-item.component.html b/src/app/item-page/simple/item-types/untyped-item/untyped-item.component.html index ff98057334..904b7e039c 100644 --- a/src/app/item-page/simple/item-types/untyped-item/untyped-item.component.html +++ b/src/app/item-page/simple/item-types/untyped-item/untyped-item.component.html @@ -18,7 +18,7 @@
- +
diff --git a/src/app/item-page/simple/metadata-representation-list/metadata-representation-list.component.html b/src/app/item-page/simple/metadata-representation-list/metadata-representation-list.component.html index 65660eaa34..efbe9206d1 100644 --- a/src/app/item-page/simple/metadata-representation-list/metadata-representation-list.component.html +++ b/src/app/item-page/simple/metadata-representation-list/metadata-representation-list.component.html @@ -7,12 +7,12 @@
- {{'item.page.related-items.view-more' | - translate:{ amount: (total - (objects.length * incrementBy) < incrementBy) ? total - (objects.length * incrementBy) : incrementBy } }} +
- {{'item.page.related-items.view-less' | - translate:{ amount: representations?.length } }} +
diff --git a/src/app/item-page/simple/related-items/related-items.component.html b/src/app/item-page/simple/related-items/related-items.component.html index 0d1e14941d..bee1f345fd 100644 --- a/src/app/item-page/simple/related-items/related-items.component.html +++ b/src/app/item-page/simple/related-items/related-items.component.html @@ -7,12 +7,12 @@
- {{'item.page.related-items.view-more' | - translate:{ amount: (itemsRD?.payload?.totalElements - (incrementBy * objects.length) < incrementBy) ? itemsRD?.payload?.totalElements - (incrementBy * objects.length) : incrementBy } }} +
- {{'item.page.related-items.view-less' | - translate:{ amount: itemsRD?.payload?.page?.length } }} +
diff --git a/src/app/item-page/versions/item-versions.component.spec.ts b/src/app/item-page/versions/item-versions.component.spec.ts index 999176d996..e46f1585d7 100644 --- a/src/app/item-page/versions/item-versions.component.spec.ts +++ b/src/app/item-page/versions/item-versions.component.spec.ts @@ -18,7 +18,7 @@ import { PaginationServiceStub } from '../../shared/testing/pagination-service.s import { AuthService } from '../../core/auth/auth.service'; import { VersionDataService } from '../../core/data/version-data.service'; import { ItemDataService } from '../../core/data/item-data.service'; -import { FormBuilder, FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { UntypedFormBuilder, FormsModule, ReactiveFormsModule } from '@angular/forms'; import { NotificationsService } from '../../shared/notifications/notifications.service'; import { NotificationsServiceStub } from '../../shared/testing/notifications-service.stub'; import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service'; @@ -140,7 +140,7 @@ describe('ItemVersionsComponent', () => { imports: [TranslateModule.forRoot(), CommonModule, FormsModule, ReactiveFormsModule, BrowserModule, ItemSharedModule], providers: [ {provide: PaginationService, useValue: new PaginationServiceStub()}, - {provide: FormBuilder, useValue: new FormBuilder()}, + {provide: UntypedFormBuilder, useValue: new UntypedFormBuilder()}, {provide: NotificationsService, useValue: new NotificationsServiceStub()}, {provide: AuthService, useValue: authenticationServiceSpy}, {provide: AuthorizationDataService, useValue: authorizationServiceSpy}, diff --git a/src/app/item-page/versions/item-versions.component.ts b/src/app/item-page/versions/item-versions.component.ts index e0fc623f49..092f4fd222 100644 --- a/src/app/item-page/versions/item-versions.component.ts +++ b/src/app/item-page/versions/item-versions.component.ts @@ -32,7 +32,7 @@ import { getItemPageRoute, getItemVersionRoute } from '../item-page-routing-paths'; -import { FormBuilder } from '@angular/forms'; +import { UntypedFormBuilder } from '@angular/forms'; import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; import { ItemVersionsSummaryModalComponent } from './item-versions-summary-modal/item-versions-summary-modal.component'; import { NotificationsService } from '../../shared/notifications/notifications.service'; @@ -171,7 +171,7 @@ export class ItemVersionsComponent implements OnInit { private versionService: VersionDataService, private itemService: ItemDataService, private paginationService: PaginationService, - private formBuilder: FormBuilder, + private formBuilder: UntypedFormBuilder, private modalService: NgbModal, private notificationsService: NotificationsService, private translateService: TranslateService, diff --git a/src/app/my-dspace-page/my-dspace-search.module.ts b/src/app/my-dspace-page/my-dspace-search.module.ts index f3775214d5..71d1343a30 100644 --- a/src/app/my-dspace-page/my-dspace-search.module.ts +++ b/src/app/my-dspace-page/my-dspace-search.module.ts @@ -23,7 +23,6 @@ import { ItemDetailPreviewComponent } from '../shared/object-detail/my-dspace-re import { ItemDetailPreviewFieldComponent } from '../shared/object-detail/my-dspace-result-detail-element/item-detail-preview/item-detail-preview-field/item-detail-preview-field.component'; import { ItemListPreviewComponent } from '../shared/object-list/my-dspace-result-list-element/item-list-preview/item-list-preview.component'; import { ThemedItemListPreviewComponent } from '../shared/object-list/my-dspace-result-list-element/item-list-preview/themed-item-list-preview.component'; -import { MyDSpaceItemStatusComponent } from '../shared/object-collection/shared/mydspace-item-status/my-dspace-item-status.component'; import { JournalEntitiesModule } from '../entity-groups/journal-entities/journal-entities.module'; import { MyDSpaceActionsModule } from '../shared/mydspace-actions/mydspace-actions.module'; import { ClaimedDeclinedTaskSearchResultListElementComponent } from '../shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-declined-task-search-result/claimed-declined-task-search-result-list-element.component'; @@ -52,7 +51,6 @@ const DECLARATIONS = [ ItemDetailPreviewFieldComponent, ItemListPreviewComponent, ThemedItemListPreviewComponent, - MyDSpaceItemStatusComponent, ]; @NgModule({ diff --git a/src/app/process-page/form/process-parameters/process-parameters.component.html b/src/app/process-page/form/process-parameters/process-parameters.component.html index d7bbce6779..a937524e88 100644 --- a/src/app/process-page/form/process-parameters/process-parameters.component.html +++ b/src/app/process-page/form/process-parameters/process-parameters.component.html @@ -1,11 +1,11 @@ -
- - +
+ +
diff --git a/src/app/process-page/form/process-parameters/process-parameters.component.spec.ts b/src/app/process-page/form/process-parameters/process-parameters.component.spec.ts index 91bc61e02f..b44479c14a 100644 --- a/src/app/process-page/form/process-parameters/process-parameters.component.spec.ts +++ b/src/app/process-page/form/process-parameters/process-parameters.component.spec.ts @@ -14,14 +14,14 @@ import { TranslateLoaderMock } from '../../../shared/mocks/translate-loader.mock describe('ProcessParametersComponent', () => { let component: ProcessParametersComponent; let fixture: ComponentFixture; - let parameterValues; - let script; + let mockParameterValues: ProcessParameter[]; + let mockScript: Script; - function init() { + function initParametersAndScriptMockValues() { const param1 = new ScriptParameter(); const param2 = new ScriptParameter(); - script = Object.assign(new Script(), { parameters: [param1, param2] }); - parameterValues = [ + mockScript = Object.assign(new Script(), { parameters: [param1, param2] }); + mockParameterValues = [ Object.assign(new ProcessParameter(), { name: '-a', value: 'bla' }), Object.assign(new ProcessParameter(), { name: '-b', value: '123' }), Object.assign(new ProcessParameter(), { name: '-c', value: 'value' }), @@ -29,7 +29,6 @@ describe('ProcessParametersComponent', () => { } beforeEach(waitForAsync(() => { - init(); TestBed.configureTestingModule({ imports: [ FormsModule, @@ -48,17 +47,34 @@ describe('ProcessParametersComponent', () => { beforeEach(() => { fixture = TestBed.createComponent(ProcessParametersComponent); component = fixture.componentInstance; - component.script = script; - component.parameterValues = parameterValues; - fixture.detectChanges(); }); it('should create', () => { expect(component).toBeTruthy(); }); - it('should render a ParameterSelectComponent for each parameter value of the component', () => { - const selectComponents = fixture.debugElement.queryAll(By.directive(ParameterSelectComponent)); - expect(selectComponents.length).toBe(parameterValues.length); + describe('when parameter values and script are initialized', () => { + + beforeEach(() => { + initParametersAndScriptMockValues(); + component.parameterValues = mockParameterValues; + component.script = mockScript; + + fixture.detectChanges(); + }); + + it(`should render a ${ParameterSelectComponent.name} for each parameter value`, () => { + const selectComponents = fixture.debugElement.queryAll(By.directive(ParameterSelectComponent)); + expect(selectComponents.length).toBe(mockParameterValues.length); + }); + + it('should not render a selector box if the parameter array is empty',() => { + fixture.componentInstance.script.parameters = []; + + fixture.detectChanges(); + + const formGroupComponent = fixture.debugElement.query(By.css('[data-testID=parameters-select-container]')); + expect(formGroupComponent).toBeFalsy(); + }); }); }); diff --git a/src/app/process-page/process-page-shared.module.ts b/src/app/process-page/process-page-shared.module.ts new file mode 100644 index 0000000000..e666283e03 --- /dev/null +++ b/src/app/process-page/process-page-shared.module.ts @@ -0,0 +1,48 @@ +import { NgModule } from '@angular/core'; +import { SharedModule } from '../shared/shared.module'; +import { NewProcessComponent } from './new/new-process.component'; +import { ScriptsSelectComponent } from './form/scripts-select/scripts-select.component'; +import { ScriptHelpComponent } from './form/script-help/script-help.component'; +import { ParameterSelectComponent } from './form/process-parameters/parameter-select/parameter-select.component'; +import { ProcessParametersComponent } from './form/process-parameters/process-parameters.component'; +import { StringValueInputComponent } from './form/process-parameters/parameter-value-input/string-value-input/string-value-input.component'; +import { ParameterValueInputComponent } from './form/process-parameters/parameter-value-input/parameter-value-input.component'; +import { FileValueInputComponent } from './form/process-parameters/parameter-value-input/file-value-input/file-value-input.component'; +import { BooleanValueInputComponent } from './form/process-parameters/parameter-value-input/boolean-value-input/boolean-value-input.component'; +import { DateValueInputComponent } from './form/process-parameters/parameter-value-input/date-value-input/date-value-input.component'; +import { ProcessOverviewComponent } from './overview/process-overview.component'; +import { ProcessDetailComponent } from './detail/process-detail.component'; +import { ProcessDetailFieldComponent } from './detail/process-detail-field/process-detail-field.component'; +import { ProcessBreadcrumbsService } from './process-breadcrumbs.service'; +import { ProcessBreadcrumbResolver } from './process-breadcrumb.resolver'; +import { ProcessFormComponent } from './form/process-form.component'; + +@NgModule({ + imports: [ + SharedModule, + ], + declarations: [ + NewProcessComponent, + ScriptsSelectComponent, + ScriptHelpComponent, + ParameterSelectComponent, + ProcessParametersComponent, + StringValueInputComponent, + ParameterValueInputComponent, + FileValueInputComponent, + BooleanValueInputComponent, + DateValueInputComponent, + ProcessOverviewComponent, + ProcessDetailComponent, + ProcessDetailFieldComponent, + ProcessFormComponent + ], + providers: [ + ProcessBreadcrumbResolver, + ProcessBreadcrumbsService + ] +}) + +export class ProcessPageSharedModule { + +} diff --git a/src/app/process-page/process-page.module.ts b/src/app/process-page/process-page.module.ts index ebe03e4537..2587813998 100644 --- a/src/app/process-page/process-page.module.ts +++ b/src/app/process-page/process-page.module.ts @@ -1,47 +1,17 @@ import { NgModule } from '@angular/core'; import { SharedModule } from '../shared/shared.module'; import { ProcessPageRoutingModule } from './process-page-routing.module'; -import { NewProcessComponent } from './new/new-process.component'; -import { ScriptsSelectComponent } from './form/scripts-select/scripts-select.component'; -import { ScriptHelpComponent } from './form/script-help/script-help.component'; -import { ParameterSelectComponent } from './form/process-parameters/parameter-select/parameter-select.component'; -import { ProcessParametersComponent } from './form/process-parameters/process-parameters.component'; -import { StringValueInputComponent } from './form/process-parameters/parameter-value-input/string-value-input/string-value-input.component'; -import { ParameterValueInputComponent } from './form/process-parameters/parameter-value-input/parameter-value-input.component'; -import { FileValueInputComponent } from './form/process-parameters/parameter-value-input/file-value-input/file-value-input.component'; -import { BooleanValueInputComponent } from './form/process-parameters/parameter-value-input/boolean-value-input/boolean-value-input.component'; -import { DateValueInputComponent } from './form/process-parameters/parameter-value-input/date-value-input/date-value-input.component'; -import { ProcessOverviewComponent } from './overview/process-overview.component'; -import { ProcessDetailComponent } from './detail/process-detail.component'; -import { ProcessDetailFieldComponent } from './detail/process-detail-field/process-detail-field.component'; -import { ProcessBreadcrumbsService } from './process-breadcrumbs.service'; -import { ProcessBreadcrumbResolver } from './process-breadcrumb.resolver'; -import { ProcessFormComponent } from './form/process-form.component'; +import { ProcessPageSharedModule } from './process-page-shared.module'; @NgModule({ imports: [ ProcessPageRoutingModule, SharedModule, + ProcessPageSharedModule, ], declarations: [ - NewProcessComponent, - ScriptsSelectComponent, - ScriptHelpComponent, - ParameterSelectComponent, - ProcessParametersComponent, - StringValueInputComponent, - ParameterValueInputComponent, - FileValueInputComponent, - BooleanValueInputComponent, - DateValueInputComponent, - ProcessOverviewComponent, - ProcessDetailComponent, - ProcessDetailFieldComponent, - ProcessFormComponent ], providers: [ - ProcessBreadcrumbResolver, - ProcessBreadcrumbsService ] }) diff --git a/src/app/profile-page/profile-page-metadata-form/profile-page-metadata-form.component.ts b/src/app/profile-page/profile-page-metadata-form/profile-page-metadata-form.component.ts index 474e6fb025..c4e164a7e8 100644 --- a/src/app/profile-page/profile-page-metadata-form/profile-page-metadata-form.component.ts +++ b/src/app/profile-page/profile-page-metadata-form/profile-page-metadata-form.component.ts @@ -5,7 +5,7 @@ import { DynamicInputModel, DynamicSelectModel } from '@ng-dynamic-forms/core'; -import { FormGroup } from '@angular/forms'; +import { UntypedFormGroup } from '@angular/forms'; import { EPerson } from '../../core/eperson/models/eperson.model'; import { TranslateService } from '@ngx-translate/core'; import { hasValue, isNotEmpty } from '../../shared/empty.util'; @@ -80,7 +80,7 @@ export class ProfilePageMetadataFormComponent implements OnInit { /** * The form group of this form */ - formGroup: FormGroup; + formGroup: UntypedFormGroup; /** * Prefix for the form's label messages of this component diff --git a/src/app/profile-page/profile-page-security-form/profile-page-security-form.component.ts b/src/app/profile-page/profile-page-security-form/profile-page-security-form.component.ts index 29bcffe4e4..04292ea96e 100644 --- a/src/app/profile-page/profile-page-security-form/profile-page-security-form.component.ts +++ b/src/app/profile-page/profile-page-security-form/profile-page-security-form.component.ts @@ -1,7 +1,7 @@ import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; import { DynamicFormControlModel, DynamicFormService, DynamicInputModel } from '@ng-dynamic-forms/core'; import { TranslateService } from '@ngx-translate/core'; -import { FormGroup } from '@angular/forms'; +import { UntypedFormGroup } from '@angular/forms'; import { hasValue, isEmpty } from '../../shared/empty.util'; import { EPersonDataService } from '../../core/eperson/eperson-data.service'; import { NotificationsService } from '../../shared/notifications/notifications.service'; @@ -51,7 +51,7 @@ export class ProfilePageSecurityFormComponent implements OnInit { /** * The form group of this form */ - formGroup: FormGroup; + formGroup: UntypedFormGroup; /** * Indicates whether the "checkPasswordEmpty" needs to be added or not @@ -127,7 +127,7 @@ export class ProfilePageSecurityFormComponent implements OnInit { * Check if both password fields are filled in and equal * @param group The FormGroup to validate */ - checkPasswordsEqual(group: FormGroup) { + checkPasswordsEqual(group: UntypedFormGroup) { const pass = group.get('password').value; const repeatPass = group.get('passwordrepeat').value; @@ -138,7 +138,7 @@ export class ProfilePageSecurityFormComponent implements OnInit { * Checks if the password is empty * @param group The FormGroup to validate */ - checkPasswordEmpty(group: FormGroup) { + checkPasswordEmpty(group: UntypedFormGroup) { const pass = group.get('password').value; return isEmpty(pass) ? { emptyPassword: true } : null; } diff --git a/src/app/register-email-form/register-email-form.component.spec.ts b/src/app/register-email-form/register-email-form.component.spec.ts index 6136db4aec..9e852d9491 100644 --- a/src/app/register-email-form/register-email-form.component.spec.ts +++ b/src/app/register-email-form/register-email-form.component.spec.ts @@ -4,7 +4,7 @@ import { RestResponse } from '../core/cache/response.models'; import { CommonModule } from '@angular/common'; import { RouterTestingModule } from '@angular/router/testing'; import { TranslateModule } from '@ngx-translate/core'; -import { FormBuilder, ReactiveFormsModule } from '@angular/forms'; +import { UntypedFormBuilder, ReactiveFormsModule } from '@angular/forms'; import { Router } from '@angular/router'; import { NotificationsService } from '../shared/notifications/notifications.service'; import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; @@ -67,7 +67,7 @@ describe('RegisterEmailFormComponent', () => { {provide: Router, useValue: router}, {provide: EpersonRegistrationService, useValue: epersonRegistrationService}, {provide: ConfigurationDataService, useValue: configurationDataService}, - {provide: FormBuilder, useValue: new FormBuilder()}, + {provide: UntypedFormBuilder, useValue: new UntypedFormBuilder()}, {provide: NotificationsService, useValue: notificationsService}, {provide: CookieService, useValue: new CookieServiceMock()}, {provide: GoogleRecaptchaService, useValue: googleRecaptchaService}, diff --git a/src/app/register-email-form/register-email-form.component.ts b/src/app/register-email-form/register-email-form.component.ts index 9f0b186d39..ddb77b669c 100644 --- a/src/app/register-email-form/register-email-form.component.ts +++ b/src/app/register-email-form/register-email-form.component.ts @@ -3,7 +3,7 @@ import {EpersonRegistrationService} from '../core/data/eperson-registration.serv import {NotificationsService} from '../shared/notifications/notifications.service'; import {TranslateService} from '@ngx-translate/core'; import {Router} from '@angular/router'; -import { FormBuilder, FormControl, FormGroup, Validators, ValidatorFn } from '@angular/forms'; +import { UntypedFormBuilder, UntypedFormControl, UntypedFormGroup, Validators, ValidatorFn } from '@angular/forms'; import {Registration} from '../core/shared/registration.model'; import {RemoteData} from '../core/data/remote-data'; import {ConfigurationDataService} from '../core/data/configuration-data.service'; @@ -33,7 +33,7 @@ export class RegisterEmailFormComponent implements OnDestroy, OnInit { /** * The form containing the mail address */ - form: FormGroup; + form: UntypedFormGroup; /** * The message prefix @@ -79,7 +79,7 @@ export class RegisterEmailFormComponent implements OnDestroy, OnInit { private notificationService: NotificationsService, private translateService: TranslateService, private router: Router, - private formBuilder: FormBuilder, + private formBuilder: UntypedFormBuilder, private configService: ConfigurationDataService, public googleRecaptchaService: GoogleRecaptchaService, public cookieService: CookieService, @@ -102,7 +102,7 @@ export class RegisterEmailFormComponent implements OnDestroy, OnInit { Validators.pattern('^[a-zA-Z0-9.!#$%&\'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$') ]; this.form = this.formBuilder.group({ - email: new FormControl('', { + email: new UntypedFormControl('', { validators: validators, }) }); diff --git a/src/app/register-page/create-profile/confirmed.validator.spec.ts b/src/app/register-page/create-profile/confirmed.validator.spec.ts index 1be6aaf58f..b4d305fcf0 100644 --- a/src/app/register-page/create-profile/confirmed.validator.spec.ts +++ b/src/app/register-page/create-profile/confirmed.validator.spec.ts @@ -1,4 +1,4 @@ -import { FormBuilder, FormControl } from '@angular/forms'; +import { UntypedFormBuilder, UntypedFormControl } from '@angular/forms'; import { fakeAsync, waitForAsync } from '@angular/core/testing'; import { ConfirmedValidator } from './confirmed.validator'; @@ -7,9 +7,9 @@ describe('ConfirmedValidator', () => { beforeEach(waitForAsync(() => { - passwordForm = (new FormBuilder()).group({ - password: new FormControl('', {}), - confirmPassword: new FormControl('', {}) + passwordForm = (new UntypedFormBuilder()).group({ + password: new UntypedFormControl('', {}), + confirmPassword: new UntypedFormControl('', {}) }, { validator: ConfirmedValidator('password', 'confirmPassword') }); diff --git a/src/app/register-page/create-profile/confirmed.validator.ts b/src/app/register-page/create-profile/confirmed.validator.ts index 30f4f01701..914d50b540 100644 --- a/src/app/register-page/create-profile/confirmed.validator.ts +++ b/src/app/register-page/create-profile/confirmed.validator.ts @@ -1,10 +1,10 @@ -import { FormGroup } from '@angular/forms'; +import { UntypedFormGroup } from '@angular/forms'; /** * Validator used to confirm that the password and confirmed password value are the same */ export function ConfirmedValidator(controlName: string, matchingControlName: string) { - return (formGroup: FormGroup) => { + return (formGroup: UntypedFormGroup) => { const control = formGroup.controls[controlName]; const matchingControl = formGroup.controls[matchingControlName]; if (matchingControl.errors && !matchingControl.errors.confirmedValidator) { diff --git a/src/app/register-page/create-profile/create-profile.component.spec.ts b/src/app/register-page/create-profile/create-profile.component.spec.ts index 026785e59b..d5a1fea8b2 100644 --- a/src/app/register-page/create-profile/create-profile.component.spec.ts +++ b/src/app/register-page/create-profile/create-profile.component.spec.ts @@ -7,7 +7,7 @@ import { TranslateModule } from '@ngx-translate/core'; import { ActivatedRoute, Router } from '@angular/router'; import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; import { Store } from '@ngrx/store'; -import { FormBuilder, ReactiveFormsModule } from '@angular/forms'; +import { UntypedFormBuilder, ReactiveFormsModule } from '@angular/forms'; import { EPersonDataService } from '../../core/eperson/eperson-data.service'; import { NotificationsService } from '../../shared/notifications/notifications.service'; import { of as observableOf } from 'rxjs'; @@ -135,7 +135,7 @@ describe('CreateProfileComponent', () => { {provide: ActivatedRoute, useValue: route}, {provide: Store, useValue: store}, {provide: EPersonDataService, useValue: ePersonDataService}, - {provide: FormBuilder, useValue: new FormBuilder()}, + {provide: UntypedFormBuilder, useValue: new UntypedFormBuilder()}, {provide: NotificationsService, useValue: notificationsService}, {provide: EndUserAgreementService, useValue: endUserAgreementService}, ], diff --git a/src/app/register-page/create-profile/create-profile.component.ts b/src/app/register-page/create-profile/create-profile.component.ts index 0e8e0e8742..eaaa983a43 100644 --- a/src/app/register-page/create-profile/create-profile.component.ts +++ b/src/app/register-page/create-profile/create-profile.component.ts @@ -3,7 +3,7 @@ import { ActivatedRoute, Router } from '@angular/router'; import { map } from 'rxjs/operators'; import { Registration } from '../../core/shared/registration.model'; import { Observable } from 'rxjs'; -import { FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms'; +import { UntypedFormBuilder, UntypedFormControl, UntypedFormGroup, Validators } from '@angular/forms'; import { TranslateService } from '@ngx-translate/core'; import { EPersonDataService } from '../../core/eperson/eperson-data.service'; import { EPerson } from '../../core/eperson/models/eperson.model'; @@ -38,7 +38,7 @@ export class CreateProfileComponent implements OnInit { isInValidPassword = true; password: string; - userInfoForm: FormGroup; + userInfoForm: UntypedFormGroup; activeLangs: LangConfig[]; /** @@ -52,7 +52,7 @@ export class CreateProfileComponent implements OnInit { private store: Store, private router: Router, private route: ActivatedRoute, - private formBuilder: FormBuilder, + private formBuilder: UntypedFormBuilder, private notificationsService: NotificationsService, private endUserAgreementService: EndUserAgreementService ) { @@ -71,14 +71,14 @@ export class CreateProfileComponent implements OnInit { this.activeLangs = environment.languages.filter((MyLangConfig) => MyLangConfig.active === true); this.userInfoForm = this.formBuilder.group({ - firstName: new FormControl('', { + firstName: new UntypedFormControl('', { validators: [Validators.required], }), - lastName: new FormControl('', { + lastName: new UntypedFormControl('', { validators: [Validators.required], }), - contactPhone: new FormControl(''), - language: new FormControl(''), + contactPhone: new UntypedFormControl(''), + language: new UntypedFormControl(''), }); } diff --git a/src/app/root/root.component.ts b/src/app/root/root.component.ts index 2bbeec6282..3c2d65fc1f 100644 --- a/src/app/root/root.component.ts +++ b/src/app/root/root.component.ts @@ -19,6 +19,7 @@ import { environment } from '../../environments/environment'; import { slideSidebarPadding } from '../shared/animations/slide'; import { MenuID } from '../shared/menu/menu-id.model'; import { getPageInternalServerErrorRoute } from '../app-routing-paths'; +import { hasValueOperator } from '../shared/empty.util'; @Component({ selector: 'ds-root', @@ -63,8 +64,8 @@ export class RootComponent implements OnInit { ngOnInit() { this.sidebarVisible = this.menuService.isMenuVisibleWithVisibleSections(MenuID.ADMIN); - this.collapsedSidebarWidth = this.cssService.getVariable('--ds-collapsed-sidebar-width'); - this.totalSidebarWidth = this.cssService.getVariable('--ds-total-sidebar-width'); + this.collapsedSidebarWidth = this.cssService.getVariable('--ds-collapsed-sidebar-width').pipe(hasValueOperator()); + this.totalSidebarWidth = this.cssService.getVariable('--ds-total-sidebar-width').pipe(hasValueOperator()); const sidebarCollapsed = this.menuService.isMenuCollapsed(MenuID.ADMIN); this.slideSidebarOver = combineLatestObservable([sidebarCollapsed, this.windowService.isXsOrSm()]) diff --git a/src/app/search-navbar/search-navbar.component.html b/src/app/search-navbar/search-navbar.component.html index e1de59ce51..2b30507f3e 100644 --- a/src/app/search-navbar/search-navbar.component.html +++ b/src/app/search-navbar/search-navbar.component.html @@ -4,9 +4,9 @@ - +
diff --git a/src/app/search-navbar/search-navbar.component.scss b/src/app/search-navbar/search-navbar.component.scss index d5f3d8d615..cf46c25d91 100644 --- a/src/app/search-navbar/search-navbar.component.scss +++ b/src/app/search-navbar/search-navbar.component.scss @@ -1,13 +1,14 @@ input[type="text"] { margin-top: calc(-0.5 * var(--bs-font-size-base)); background-color: #fff !important; + border-color: var(--ds-header-icon-color); &.collapsed { opacity: 0; } } -a.submit-icon { +.submit-icon { cursor: pointer; position: sticky; top: 0; diff --git a/src/app/search-navbar/search-navbar.component.ts b/src/app/search-navbar/search-navbar.component.ts index ccdaa27861..98e64f6e10 100644 --- a/src/app/search-navbar/search-navbar.component.ts +++ b/src/app/search-navbar/search-navbar.component.ts @@ -1,5 +1,5 @@ import { Component, ElementRef, ViewChild } from '@angular/core'; -import { FormBuilder } from '@angular/forms'; +import { UntypedFormBuilder } from '@angular/forms'; import { Router } from '@angular/router'; import { SearchService } from '../core/shared/search/search.service'; import { expandSearchInput } from '../shared/animations/slide'; @@ -24,7 +24,7 @@ export class SearchNavbarComponent { // Search input field @ViewChild('searchInput') searchField: ElementRef; - constructor(private formBuilder: FormBuilder, private router: Router, private searchService: SearchService) { + constructor(private formBuilder: UntypedFormBuilder, private router: Router, private searchService: SearchService) { this.searchForm = this.formBuilder.group(({ query: '', })); diff --git a/src/app/shared/auth-nav-menu/auth-nav-menu.component.html b/src/app/shared/auth-nav-menu/auth-nav-menu.component.html index 94cbd4368a..05f502afa1 100644 --- a/src/app/shared/auth-nav-menu/auth-nav-menu.component.html +++ b/src/app/shared/auth-nav-menu/auth-nav-menu.component.html @@ -19,7 +19,7 @@