diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7758020724..539fd740ee 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -29,11 +29,11 @@ jobs: steps: # https://github.com/actions/checkout - name: Checkout codebase - uses: actions/checkout@v1 + uses: actions/checkout@v2 # https://github.com/actions/setup-node - name: Install Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v1 + uses: actions/setup-node@v2 with: node-version: ${{ matrix.node-version }} @@ -82,7 +82,7 @@ jobs: # Upload coverage reports to Codecov (for Node v12 only) # https://github.com/codecov/codecov-action - name: Upload coverage to Codecov.io - uses: codecov/codecov-action@v1 + uses: codecov/codecov-action@v2 if: matrix.node-version == '12.x' # Using docker-compose start backend using CI configuration diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml new file mode 100644 index 0000000000..00ec2fa8f7 --- /dev/null +++ b/.github/workflows/docker.yml @@ -0,0 +1,78 @@ +# DSpace Docker image build for hub.docker.com +name: Docker images + +# Run this Build for all pushes to 'main' or maintenance branches, or tagged releases. +# Also run for PRs to ensure PR doesn't break Docker build process +on: + push: + branches: + - main + - 'dspace-**' + tags: + - 'dspace-**' + pull_request: + +jobs: + docker: + # Ensure this job never runs on forked repos. It's only executed for 'dspace/dspace-angular' + if: github.repository == 'dspace/dspace-angular' + runs-on: ubuntu-latest + env: + # Define tags to use for Docker images based on Git tags/branches (for docker/metadata-action) + # For a new commit on default branch (main), use the literal tag 'dspace-7_x' on Docker image. + # For a new commit on other branches, use the branch name as the tag for Docker image. + # For a new tag, copy that tag name as the tag for Docker image. + IMAGE_TAGS: | + type=raw,value=dspace-7_x,enable=${{ endsWith(github.ref, github.event.repository.default_branch) }} + type=ref,event=branch,enable=${{ !endsWith(github.ref, github.event.repository.default_branch) }} + type=ref,event=tag + # Define default tag "flavor" for docker/metadata-action per + # https://github.com/docker/metadata-action#flavor-input + # We turn off 'latest' tag by default. + TAGS_FLAVOR: | + latest=false + + steps: + # https://github.com/actions/checkout + - name: Checkout codebase + uses: actions/checkout@v2 + + # https://github.com/docker/setup-buildx-action + - name: Setup Docker Buildx + uses: docker/setup-buildx-action@v1 + + # https://github.com/docker/login-action + - name: Login to DockerHub + # Only login if not a PR, as PRs only trigger a Docker build and not a push + if: github.event_name != 'pull_request' + uses: docker/login-action@v1 + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_ACCESS_TOKEN }} + + ############################################### + # Build/Push the 'dspace/dspace-angular' image + ############################################### + # https://github.com/docker/metadata-action + # Get Metadata for docker_build step below + - name: Sync metadata (tags, labels) from GitHub to Docker for 'dspace-angular' image + id: meta_build + uses: docker/metadata-action@v3 + with: + images: dspace/dspace-angular + tags: ${{ env.IMAGE_TAGS }} + flavor: ${{ env.TAGS_FLAVOR }} + + # https://github.com/docker/build-push-action + - name: Build and push 'dspace-angular' image + id: docker_build + uses: docker/build-push-action@v2 + with: + context: . + file: ./Dockerfile + # For pull requests, we run the Docker build (to ensure no PR changes break the build), + # but we ONLY do an image push to DockerHub if it's NOT a PR + push: ${{ github.event_name != 'pull_request' }} + # Use tags / labels provided by 'docker/metadata-action' above + tags: ${{ steps.meta_build.outputs.tags }} + labels: ${{ steps.meta_build.outputs.labels }} diff --git a/.vscode/settings.json b/.vscode/settings.json index e8522b85d7..f60eb01f00 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -3,5 +3,6 @@ "i18n-ally.localesPaths": [ "src/assets/i18n", "src/app/core/locale" - ] + ], + "typescript.tsdk": "node_modules\\typescript\\lib" } \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index db9983cace..2d98971112 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,7 @@ # This image will be published as dspace/dspace-angular -# See https://dspace-labs.github.io/DSpace-Docker-Images/ for usage details +# See https://github.com/DSpace/dspace-angular/tree/main/docker for usage details -FROM node:12-alpine +FROM node:14-alpine WORKDIR /app ADD . /app/ EXPOSE 4000 diff --git a/LICENSE b/LICENSE index f55d21fe42..b381f6d968 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -DSpace source code BSD License: +BSD 3-Clause License Copyright (c) 2002-2021, LYRASIS. All rights reserved. @@ -13,13 +13,12 @@ notice, this list of conditions and the following disclaimer. notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. -- Neither the name DuraSpace nor the name of the DSpace Foundation -nor the names of its contributors may be used to endorse or promote -products derived from this software without specific prior written -permission. +- Neither the name of the copyright holder nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, @@ -29,11 +28,4 @@ OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH -DAMAGE. - - -DSpace uses third-party libraries which may be distributed under -different licenses to the above. Information about these licenses -is detailed in the LICENSES_THIRD_PARTY file at the root of the source -tree. You must agree to the terms of these licenses, in addition to -the above DSpace source code license, in order to use this software. +DAMAGE. \ No newline at end of file diff --git a/NOTICE b/NOTICE new file mode 100644 index 0000000000..44bbf95d2a --- /dev/null +++ b/NOTICE @@ -0,0 +1,28 @@ +Licenses of Third-Party Libraries +================================= + +DSpace uses third-party libraries which may be distributed under +different licenses than specified in our LICENSE file. Information +about these licenses is detailed in the LICENSES_THIRD_PARTY file at +the root of the source tree. You must agree to the terms of these +licenses, in addition to the DSpace source code license, in order to +use this software. + +Licensing Notices +================= + +[July 2019] DuraSpace joined with LYRASIS (another 501(c)3 organization) in July 2019. +LYRASIS holds the copyrights of DuraSpace. + +[July 2009] Fedora Commons joined with the DSpace Foundation and began operating under +the new name DuraSpace in July 2009. DuraSpace holds the copyrights of +the DSpace Foundation, Inc. + +[July 2007] The DSpace Foundation, Inc. is a 501(c)3 corporation established in July 2007 +with a mission to promote and advance the dspace platform enabling management, +access and preservation of digital works. The Foundation was able to transfer +the legal copyright from Hewlett-Packard Company (HP) and Massachusetts +Institute of Technology (MIT) to the DSpace Foundation in October 2007. Many +of the files in the source code may contain a copyright statement stating HP +and MIT possess the copyright, in these instances please note that the copy +right has transferred to the DSpace foundation, and subsequently to DuraSpace. \ No newline at end of file diff --git a/README.md b/README.md index 69b6132478..1143a79f31 100644 --- a/README.md +++ b/README.md @@ -449,4 +449,8 @@ DSpace uses GitHub to track issues: License ------- -This project's source code is made available under the DSpace BSD License: http://www.dspace.org/license +DSpace source code is freely available under a standard [BSD 3-Clause license](https://opensource.org/licenses/BSD-3-Clause). +The full license is available in the [LICENSE](LICENSE) file or online at http://www.dspace.org/license/ + +DSpace uses third-party libraries which may be distributed under different licenses. Those licenses are listed +in the [LICENSES_THIRD_PARTY](LICENSES_THIRD_PARTY) file. diff --git a/docker/README.md b/docker/README.md index 747db22143..b0943562af 100644 --- a/docker/README.md +++ b/docker/README.md @@ -4,6 +4,20 @@ :warning: **NOT PRODUCTION READY** The below Docker Compose resources are not guaranteed "production ready" at this time. They have been built for development/testing only. Therefore, DSpace Docker images may not be fully secured or up-to-date. While you are welcome to base your own images on these DSpace images/resources, these should not be used "as is" in any production scenario. *** +## 'Dockerfile' in root directory +This Dockerfile is used to build a *development* DSpace 7 Angular UI image, published as 'dspace/dspace-angular' + +``` +docker build -t dspace/dspace-angular:dspace-7_x . +``` + +This image is built *automatically* after each commit is made to the `main` branch. + +Admins to our DockerHub repo can manually publish with the following command. +``` +docker push dspace/dspace-angular:dspace-7_x +``` + ## docker directory - docker-compose.yml - Starts DSpace Angular with Docker Compose from the current branch. This file assumes that a DSpace 7 REST instance will also be started in Docker. diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 7c5c326959..e518dc99d2 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -20,7 +20,7 @@ services: DSPACE_NAMESPACE: / DSPACE_PORT: '4000' DSPACE_SSL: "false" - image: dspace/dspace-angular:latest + image: dspace/dspace-angular:dspace-7_x build: context: .. dockerfile: Dockerfile diff --git a/package.json b/package.json index b901c8d2db..236845da1a 100644 --- a/package.json +++ b/package.json @@ -63,26 +63,26 @@ "webdriver-manager": "^12.1.8" }, "dependencies": { - "@angular/animations": "~10.2.3", - "@angular/cdk": "^10.2.6", - "@angular/common": "~10.2.3", - "@angular/compiler": "~10.2.3", - "@angular/core": "~10.2.3", - "@angular/forms": "~10.2.3", - "@angular/localize": "10.2.3", - "@angular/platform-browser": "~10.2.3", - "@angular/platform-browser-dynamic": "~10.2.3", - "@angular/platform-server": "~10.2.3", - "@angular/router": "~10.2.3", + "@angular/animations": "~11.2.14", + "@angular/cdk": "^11.2.13", + "@angular/common": "~11.2.14", + "@angular/compiler": "~11.2.14", + "@angular/core": "~11.2.14", + "@angular/forms": "~11.2.14", + "@angular/localize": "11.2.14", + "@angular/platform-browser": "~11.2.14", + "@angular/platform-browser-dynamic": "~11.2.14", + "@angular/platform-server": "~11.2.14", + "@angular/router": "~11.2.14", "@angularclass/bootloader": "1.0.1", "@kolkov/ngx-gallery": "^1.2.3", - "@ng-bootstrap/ng-bootstrap": "7.0.0", - "@ng-dynamic-forms/core": "^12.0.0", - "@ng-dynamic-forms/ui-ng-bootstrap": "^12.0.0", - "@ngrx/effects": "^10.0.1", - "@ngrx/router-store": "^10.0.1", - "@ngrx/store": "^10.0.1", - "@nguniversal/express-engine": "10.1.0", + "@ng-bootstrap/ng-bootstrap": "9.1.3", + "@ng-dynamic-forms/core": "^13.0.0", + "@ng-dynamic-forms/ui-ng-bootstrap": "^13.0.0", + "@ngrx/effects": "^11.1.1", + "@ngrx/router-store": "^11.1.1", + "@ngrx/store": "^11.1.1", + "@nguniversal/express-engine": "11.2.1", "@ngx-translate/core": "^13.0.0", "@nicky-lenaers/ngx-scroll-to": "^9.0.0", "angular-idle-preload": "3.0.0", @@ -115,13 +115,13 @@ "mirador-share-plugin": "^0.10.0", "moment": "^2.29.1", "morgan": "^1.10.0", - "ng-mocks": "10.5.4", + "ng-mocks": "11.11.2", "ng2-file-upload": "1.4.0", "ng2-nouislider": "^1.8.3", "ngx-infinite-scroll": "^10.0.1", "ngx-moment": "^5.0.0", "ngx-pagination": "5.0.0", - "ngx-sortablejs": "^10.0.0", + "ngx-sortablejs": "^11.1.0", "nouislider": "^14.6.3", "pem": "1.14.4", "postcss-cli": "^8.3.0", @@ -138,25 +138,25 @@ }, "devDependencies": { "@angular-builders/custom-webpack": "10.0.1", - "@angular-devkit/build-angular": "~0.1002.3", - "@angular/cli": "~10.2.0", - "@angular/compiler-cli": "~10.2.3", - "@angular/language-service": "~10.2.3", + "@angular-devkit/build-angular": "~0.1102.15", + "@angular/cli": "~11.2.15", + "@angular/compiler-cli": "~11.2.14", + "@angular/language-service": "~11.2.14", "@cypress/schematic": "^1.5.0", "@fortawesome/fontawesome-free": "^5.5.0", - "@ngrx/store-devtools": "^10.0.1", + "@ngrx/store-devtools": "^11.1.1", "@ngtools/webpack": "10.2.3", - "@nguniversal/builders": "~10.1.0", + "@nguniversal/builders": "~11.2.1", "@types/deep-freeze": "0.1.2", "@types/express": "^4.17.9", "@types/file-saver": "^2.0.1", - "@types/jasmine": "^3.6.2", + "@types/jasmine": "~3.6.0", "@types/jasminewd2": "~2.0.8", "@types/js-cookie": "2.2.6", "@types/lodash": "^4.14.165", "@types/node": "^14.14.9", "axe-core": "^4.3.3", - "codelyzer": "^6.0.1", + "codelyzer": "^6.0.0", "compression-webpack-plugin": "^3.0.1", "copy-webpack-plugin": "^6.4.1", "css-loader": "3.4.0", @@ -169,14 +169,14 @@ "fork-ts-checker-webpack-plugin": "^6.0.3", "html-loader": "^1.3.2", "html-webpack-plugin": "^4.5.0", - "jasmine-core": "^3.6.0", + "jasmine-core": "~3.6.0", "jasmine-marbles": "0.6.0", - "jasmine-spec-reporter": "^6.0.0", + "jasmine-spec-reporter": "~5.0.0", "karma": "^5.2.3", - "karma-chrome-launcher": "^3.1.0", + "karma-chrome-launcher": "~3.1.0", "karma-coverage-istanbul-reporter": "~3.0.2", - "karma-jasmine": "^4.0.1", - "karma-jasmine-html-reporter": "^1.5.4", + "karma-jasmine": "~4.0.0", + "karma-jasmine-html-reporter": "^1.5.0", "karma-mocha-reporter": "2.2.5", "nodemon": "^2.0.2", "npm-run-all": "^4.1.5", @@ -204,4 +204,4 @@ "webpack-cli": "^4.2.0", "webpack-dev-server": "^4.5.0" } -} +} \ No newline at end of file diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index 49e96a028a..3a47815f42 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -216,7 +216,8 @@ import { ServerCheckGuard } from './core/server-check/server-check.guard'; } ], { onSameUrlNavigation: 'reload', - }) + relativeLinkResolution: 'legacy' +}) ], exports: [RouterModule], }) diff --git a/src/app/collection-page/collection-item-mapper/collection-item-mapper.component.html b/src/app/collection-page/collection-item-mapper/collection-item-mapper.component.html index e10b9da247..042beddc84 100644 --- a/src/app/collection-page/collection-item-mapper/collection-item-mapper.component.html +++ b/src/app/collection-page/collection-item-mapper/collection-item-mapper.component.html @@ -5,9 +5,10 @@

{{'collection.edit.item-mapper.description' | translate}}

- - - + +
diff --git a/src/app/item-page/edit-item-page/item-collection-mapper/item-collection-mapper.component.html b/src/app/item-page/edit-item-page/item-collection-mapper/item-collection-mapper.component.html index 589981d3c9..c779c4aafa 100644 --- a/src/app/item-page/edit-item-page/item-collection-mapper/item-collection-mapper.component.html +++ b/src/app/item-page/edit-item-page/item-collection-mapper/item-collection-mapper.component.html @@ -5,9 +5,10 @@

{{'item.edit.item-mapper.description' | translate}}

- - - + +
diff --git a/src/app/item-page/media-viewer/media-viewer-image/media-viewer-image.component.spec.ts b/src/app/item-page/media-viewer/media-viewer-image/media-viewer-image.component.spec.ts index 1f1fed789f..98d0ac08a6 100644 --- a/src/app/item-page/media-viewer/media-viewer-image/media-viewer-image.component.spec.ts +++ b/src/app/item-page/media-viewer/media-viewer-image/media-viewer-image.component.spec.ts @@ -1,5 +1,5 @@ import { NO_ERRORS_SCHEMA } from '@angular/core'; -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { NgxGalleryOptions } from '@kolkov/ngx-gallery'; import { Bitstream } from '../../../core/shared/bitstream.model'; import { MediaViewerItem } from '../../../core/shared/media-viewer-item.model'; @@ -55,7 +55,7 @@ describe('MediaViewerImageComponent', () => { ] ); - beforeEach(async(() => { + beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ imports:[], declarations: [MediaViewerImageComponent], diff --git a/src/app/item-page/media-viewer/media-viewer-video/media-viewer-video.component.spec.ts b/src/app/item-page/media-viewer/media-viewer-video/media-viewer-video.component.spec.ts index 88138a252f..354ba57727 100644 --- a/src/app/item-page/media-viewer/media-viewer-video/media-viewer-video.component.spec.ts +++ b/src/app/item-page/media-viewer/media-viewer-video/media-viewer-video.component.spec.ts @@ -1,5 +1,5 @@ import { NO_ERRORS_SCHEMA } from '@angular/core'; -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; import { of as observableOf } from 'rxjs'; @@ -17,7 +17,7 @@ describe('MediaViewerVideoComponent', () => { let component: MediaViewerVideoComponent; let fixture: ComponentFixture; - beforeEach(async(() => { + beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ imports: [ TranslateModule.forRoot({ diff --git a/src/app/item-page/media-viewer/media-viewer.component.spec.ts b/src/app/item-page/media-viewer/media-viewer.component.spec.ts index ebea703ec8..cfd72bf416 100644 --- a/src/app/item-page/media-viewer/media-viewer.component.spec.ts +++ b/src/app/item-page/media-viewer/media-viewer.component.spec.ts @@ -1,4 +1,4 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { Bitstream } from '../../core/shared/bitstream.model'; import { createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils'; import { createPaginatedList } from '../../shared/testing/utils.test'; @@ -60,7 +60,7 @@ describe('MediaViewerComponent', () => { { bitstream: mockBitstream, format: 'image', thumbnail: null } ); - beforeEach(async(() => { + beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ imports: [ TranslateModule.forRoot({ diff --git a/src/app/item-page/simple/related-entities/tabbed-related-entities-search/tabbed-related-entities-search.component.html b/src/app/item-page/simple/related-entities/tabbed-related-entities-search/tabbed-related-entities-search.component.html index f9642d2c01..223d4a6ed0 100644 --- a/src/app/item-page/simple/related-entities/tabbed-related-entities-search/tabbed-related-entities-search.component.html +++ b/src/app/item-page/simple/related-entities/tabbed-related-entities-search/tabbed-related-entities-search.component.html @@ -1,17 +1,21 @@ - - - -
- - -
-
-
-
+ + +
+
{ }; describe('With only one Entity', () => { - beforeEach(async(() => { + beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ imports: [ CommonModule, @@ -126,7 +126,7 @@ describe('MyDSpaceNewExternalDropdownComponent test', () => { }); describe('With more than one Entity', () => { - beforeEach(async(() => { + beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ imports: [ CommonModule, diff --git a/src/app/my-dspace-page/my-dspace-new-submission/my-dspace-new-submission-dropdown/my-dspace-new-submission-dropdown.component.spec.ts b/src/app/my-dspace-page/my-dspace-new-submission/my-dspace-new-submission-dropdown/my-dspace-new-submission-dropdown.component.spec.ts index 2e7361c560..a13acd0501 100644 --- a/src/app/my-dspace-page/my-dspace-new-submission/my-dspace-new-submission-dropdown/my-dspace-new-submission-dropdown.component.spec.ts +++ b/src/app/my-dspace-page/my-dspace-new-submission/my-dspace-new-submission-dropdown/my-dspace-new-submission-dropdown.component.spec.ts @@ -1,5 +1,5 @@ import { Component, DebugElement, NO_ERRORS_SCHEMA } from '@angular/core'; -import { async, ComponentFixture, inject, TestBed } from '@angular/core/testing'; +import { ComponentFixture, inject, TestBed, waitForAsync } from '@angular/core/testing'; import { By } from '@angular/platform-browser'; import { CommonModule } from '@angular/common'; import { TranslateModule } from '@ngx-translate/core'; @@ -79,7 +79,7 @@ describe('MyDSpaceNewSubmissionDropdownComponent test', () => { }; describe('With only one Entity', () => { - beforeEach(async(() => { + beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ imports: [ CommonModule, @@ -130,7 +130,7 @@ describe('MyDSpaceNewSubmissionDropdownComponent test', () => { }); describe('With more than one Entity', () => { - beforeEach(async(() => { + beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ imports: [ CommonModule, diff --git a/src/app/shared/bitstream-download-page/bitstream-download-page.component.spec.ts b/src/app/shared/bitstream-download-page/bitstream-download-page.component.spec.ts index 2803a7f789..4100653e0f 100644 --- a/src/app/shared/bitstream-download-page/bitstream-download-page.component.spec.ts +++ b/src/app/shared/bitstream-download-page/bitstream-download-page.component.spec.ts @@ -1,4 +1,4 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { AuthService } from '../../core/auth/auth.service'; import { FileService } from '../../core/shared/file.service'; import { of as observableOf } from 'rxjs'; @@ -77,7 +77,7 @@ describe('BitstreamDownloadPageComponent', () => { } describe('init', () => { - beforeEach(async(() => { + beforeEach(waitForAsync(() => { init(); initTestbed(); })); @@ -93,7 +93,7 @@ describe('BitstreamDownloadPageComponent', () => { describe('bitstream retrieval', () => { describe('when the user is authorized and not logged in', () => { - beforeEach(async(() => { + beforeEach(waitForAsync(() => { init(); (authService.isAuthenticated as jasmine.Spy).and.returnValue(observableOf(false)); @@ -109,7 +109,7 @@ describe('BitstreamDownloadPageComponent', () => { }); }); describe('when the user is authorized and logged in', () => { - beforeEach(async(() => { + beforeEach(waitForAsync(() => { init(); initTestbed(); })); @@ -123,7 +123,7 @@ describe('BitstreamDownloadPageComponent', () => { }); }); describe('when the user is not authorized and logged in', () => { - beforeEach(async(() => { + beforeEach(waitForAsync(() => { init(); (authorizationService.isAuthorized as jasmine.Spy).and.returnValue(observableOf(false)); initTestbed(); @@ -138,7 +138,7 @@ describe('BitstreamDownloadPageComponent', () => { }); }); describe('when the user is not authorized and not logged in', () => { - beforeEach(async(() => { + beforeEach(waitForAsync(() => { init(); (authService.isAuthenticated as jasmine.Spy).and.returnValue(observableOf(false)); (authorizationService.isAuthorized as jasmine.Spy).and.returnValue(observableOf(false)); diff --git a/src/app/shared/browse-by/browse-by.component.html b/src/app/shared/browse-by/browse-by.component.html index c133324681..6d1422293d 100644 --- a/src/app/shared/browse-by/browse-by.component.html +++ b/src/app/shared/browse-by/browse-by.component.html @@ -25,7 +25,7 @@
  • - +
diff --git a/src/app/shared/browse-by/browse-by.component.spec.ts b/src/app/shared/browse-by/browse-by.component.spec.ts index 915a86e87a..896537b63a 100644 --- a/src/app/shared/browse-by/browse-by.component.spec.ts +++ b/src/app/shared/browse-by/browse-by.component.spec.ts @@ -2,7 +2,7 @@ import { BrowseByComponent } from './browse-by.component'; import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; import { By } from '@angular/platform-browser'; -import { NO_ERRORS_SCHEMA } from '@angular/core'; +import { Component, NO_ERRORS_SCHEMA } from '@angular/core'; import { of as observableOf } from 'rxjs'; import { SharedModule } from '../shared.module'; import { CommonModule } from '@angular/common'; @@ -18,17 +18,31 @@ import { PaginationComponentOptions } from '../pagination/pagination-component-o import { SortDirection, SortOptions } from '../../core/cache/models/sort-options.model'; import { createSuccessfulRemoteDataObject$ } from '../remote-data.utils'; import { storeModuleConfig } from '../../app.reducer'; -import { FindListOptions } from '../../core/data/request.models'; import { PaginationService } from '../../core/pagination/pagination.service'; import { PaginationServiceStub } from '../testing/pagination-service.stub'; +import { ListableObjectComponentLoaderComponent } from '../object-collection/shared/listable-object/listable-object-component-loader.component'; +import { ViewMode } from '../../core/shared/view-mode.model'; +import { BrowseEntryListElementComponent } from '../object-list/browse-entry-list-element/browse-entry-list-element.component'; +import { + DEFAULT_CONTEXT, + listableObjectComponent, +} from '../object-collection/shared/listable-object/listable-object.decorator'; +import { BrowseEntry } from '../../core/shared/browse-entry.model'; +import { ITEM } from '../../core/shared/item.resource-type'; import { ThemeService } from '../theme-support/theme.service'; +import SpyObj = jasmine.SpyObj; + +@listableObjectComponent(BrowseEntry, ViewMode.ListElement, DEFAULT_CONTEXT, 'custom') +@Component({ + selector: 'ds-browse-entry-list-element', + template: '' +}) +class MockThemedBrowseEntryListElementComponent {} describe('BrowseByComponent', () => { let comp: BrowseByComponent; let fixture: ComponentFixture; - let themeService: ThemeService; - const mockItems = [ Object.assign(new Item(), { id: 'fakeId-1', @@ -59,9 +73,12 @@ describe('BrowseByComponent', () => { }); const paginationService = new PaginationServiceStub(paginationConfig); + let themeService: SpyObj; + beforeEach(waitForAsync(() => { themeService = jasmine.createSpyObj('themeService', { getThemeName: 'dspace', + getThemeName$: observableOf('dspace'), }); TestBed.configureTestingModule({ imports: [ @@ -82,6 +99,7 @@ describe('BrowseByComponent', () => { declarations: [], providers: [ {provide: PaginationService, useValue: paginationService}, + {provide: MockThemedBrowseEntryListElementComponent}, { provide: ThemeService, useValue: themeService }, ], schemas: [NO_ERRORS_SCHEMA] @@ -170,4 +188,67 @@ describe('BrowseByComponent', () => { }); }); + describe('when enableArrows is true and browseEntries are provided', () => { + let browseEntries; + + beforeEach(() => { + browseEntries = [ + Object.assign(new BrowseEntry(), { + type: ITEM, + authority: 'authority key 1', + value: 'browse entry 1', + language: null, + count: 1, + }), + Object.assign(new BrowseEntry(), { + type: ITEM, + authority: null, + value: 'browse entry 2', + language: null, + count: 4, + }), + ]; + + comp.enableArrows = true; + comp.objects$ = createSuccessfulRemoteDataObject$(buildPaginatedList(new PageInfo(), browseEntries)); + comp.paginationConfig = paginationConfig; + comp.sortConfig = Object.assign(new SortOptions('dc.title', SortDirection.ASC)); + // NOTE: do NOT trigger change detection until the theme is set, such that the theme can be picked up as well + }); + + describe('when theme is base', () => { + beforeEach(() => { + themeService.getThemeName.and.returnValue('base'); + themeService.getThemeName$.and.returnValue(observableOf('base')); + fixture.detectChanges(); + }); + + it('should use the base component to render browse entries', () => { + const componentLoaders = fixture.debugElement.queryAll(By.directive(ListableObjectComponentLoaderComponent)); + expect(componentLoaders.length).toEqual(browseEntries.length); + componentLoaders.forEach((componentLoader) => { + const browseEntry = componentLoader.query(By.css('ds-browse-entry-list-element')); + expect(browseEntry.componentInstance).toBeInstanceOf(BrowseEntryListElementComponent); + }); + }); + }); + + describe('when theme is custom', () => { + beforeEach(() => { + themeService.getThemeName.and.returnValue('custom'); + themeService.getThemeName$.and.returnValue(observableOf('custom')); + fixture.detectChanges(); + }); + + it('should use the themed component to render browse entries', () => { + const componentLoaders = fixture.debugElement.queryAll(By.directive(ListableObjectComponentLoaderComponent)); + expect(componentLoaders.length).toEqual(browseEntries.length); + componentLoaders.forEach((componentLoader) => { + const browseEntry = componentLoader.query(By.css('ds-browse-entry-list-element')); + expect(browseEntry.componentInstance).toBeInstanceOf(MockThemedBrowseEntryListElementComponent); + }); + }); + }); + }); + }); diff --git a/src/app/shared/browse-by/browse-by.component.ts b/src/app/shared/browse-by/browse-by.component.ts index 1f05ad2258..861e431635 100644 --- a/src/app/shared/browse-by/browse-by.component.ts +++ b/src/app/shared/browse-by/browse-by.component.ts @@ -8,6 +8,7 @@ import { Observable } from 'rxjs'; import { ListableObject } from '../object-collection/shared/listable-object.model'; import { getStartsWithComponent, StartsWithType } from '../starts-with/starts-with-decorator'; import { PaginationService } from '../../core/pagination/pagination.service'; +import { ViewMode } from '../../core/shared/view-mode.model'; @Component({ selector: 'ds-browse-by', @@ -22,6 +23,12 @@ import { PaginationService } from '../../core/pagination/pagination.service'; * Component to display a browse-by page for any ListableObject */ export class BrowseByComponent implements OnInit { + + /** + * ViewMode that should be passed to {@link ListableObjectComponentLoaderComponent}. + */ + viewMode: ViewMode = ViewMode.ListElement; + /** * The i18n message to display as title */ diff --git a/src/app/shared/dso-selector/dso-selector/dso-selector.component.html b/src/app/shared/dso-selector/dso-selector/dso-selector.component.html index ab2ea6cd8b..4b46d3bc3f 100644 --- a/src/app/shared/dso-selector/dso-selector/dso-selector.component.html +++ b/src/app/shared/dso-selector/dso-selector/dso-selector.component.html @@ -3,7 +3,7 @@ class="form-control" (click)="$event.stopPropagation();" placeholder="{{'dso-selector.placeholder' | translate: { type: typesString } }}" - [formControl]="input" dsAutoFocus (keyup.enter)="selectSingleResult()"> + [formControl]="input" ngbAutofocus (keyup.enter)="selectSingleResult()">
diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/dynamic-lookup-relation-modal.component.html b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/dynamic-lookup-relation-modal.component.html index ab4984f2fe..058b3142c3 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/dynamic-lookup-relation-modal.component.html +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/dynamic-lookup-relation-modal.component.html @@ -7,59 +7,64 @@