mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-07 10:04:11 +00:00
Merge pull request #3907 from 4Science/task/dspace-7_x/DURACOM-303
[Port dspace-7_x] Exclude search and browse from Angular SSR
This commit is contained in:
@@ -25,6 +25,14 @@ ssr:
|
|||||||
inlineCriticalCss: false
|
inlineCriticalCss: false
|
||||||
# Path prefixes to enable SSR for. By default these are limited to paths of primary DSpace objects.
|
# Path prefixes to enable SSR for. By default these are limited to paths of primary DSpace objects.
|
||||||
paths: [ '/home', '/items/', '/entities/', '/collections/', '/communities/', '/bitstream/', '/bitstreams/', '/handle/' ]
|
paths: [ '/home', '/items/', '/entities/', '/collections/', '/communities/', '/bitstream/', '/bitstreams/', '/handle/' ]
|
||||||
|
# Whether to enable rendering of Search component on SSR.
|
||||||
|
# If set to true the component will be included in the HTML returned from the server side rendering.
|
||||||
|
# If set to false the component will not be included in the HTML returned from the server side rendering.
|
||||||
|
enableSearchComponent: false,
|
||||||
|
# Whether to enable rendering of Browse component on SSR.
|
||||||
|
# If set to true the component will be included in the HTML returned from the server side rendering.
|
||||||
|
# If set to false the component will not be included in the HTML returned from the server side rendering.
|
||||||
|
enableBrowseComponent: false,
|
||||||
|
|
||||||
# The REST API server settings
|
# The REST API server settings
|
||||||
# NOTE: these settings define which (publicly available) REST API to use. They are usually
|
# NOTE: these settings define which (publicly available) REST API to use. They are usually
|
||||||
@@ -412,3 +420,12 @@ liveRegion:
|
|||||||
messageTimeOutDurationMs: 30000
|
messageTimeOutDurationMs: 30000
|
||||||
# The visibility of the live region. Setting this to true is only useful for debugging purposes.
|
# The visibility of the live region. Setting this to true is only useful for debugging purposes.
|
||||||
isVisible: false
|
isVisible: false
|
||||||
|
|
||||||
|
|
||||||
|
# Search settings
|
||||||
|
search:
|
||||||
|
# Number used to render n UI elements called loading skeletons that act as placeholders.
|
||||||
|
# These elements indicate that some content will be loaded in their stead.
|
||||||
|
# Since we don't know how many filters will be loaded before we receive a response from the server we use this parameter for the skeletons count.
|
||||||
|
# e.g. If we set 5 then 5 loading skeletons will be visualized before the actual filters are retrieved.
|
||||||
|
defaultFiltersCount: 5
|
||||||
|
@@ -111,6 +111,7 @@
|
|||||||
"ngx-infinite-scroll": "^15.0.0",
|
"ngx-infinite-scroll": "^15.0.0",
|
||||||
"ngx-pagination": "6.0.3",
|
"ngx-pagination": "6.0.3",
|
||||||
"ngx-sortablejs": "^11.1.0",
|
"ngx-sortablejs": "^11.1.0",
|
||||||
|
"ngx-skeleton-loader": "^7.0.0",
|
||||||
"ngx-ui-switch": "^14.1.0",
|
"ngx-ui-switch": "^14.1.0",
|
||||||
"nouislider": "^15.8.1",
|
"nouislider": "^15.8.1",
|
||||||
"pem": "1.14.8",
|
"pem": "1.14.8",
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import { BrowseByDatePageComponent } from './browse-by-date-page.component';
|
import { BrowseByDatePageComponent } from './browse-by-date-page.component';
|
||||||
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
import { ComponentFixture, fakeAsync, TestBed, tick, waitForAsync } from '@angular/core/testing';
|
||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
import { RouterTestingModule } from '@angular/router/testing';
|
import { RouterTestingModule } from '@angular/router/testing';
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
@@ -9,7 +9,7 @@ import { ActivatedRoute, Router } from '@angular/router';
|
|||||||
import { BrowseService } from '../../core/browse/browse.service';
|
import { BrowseService } from '../../core/browse/browse.service';
|
||||||
import { DSpaceObjectDataService } from '../../core/data/dspace-object-data.service';
|
import { DSpaceObjectDataService } from '../../core/data/dspace-object-data.service';
|
||||||
import { RouterMock } from '../../shared/mocks/router.mock';
|
import { RouterMock } from '../../shared/mocks/router.mock';
|
||||||
import { ChangeDetectorRef, NO_ERRORS_SCHEMA } from '@angular/core';
|
import { ChangeDetectorRef, NO_ERRORS_SCHEMA, PLATFORM_ID } from '@angular/core';
|
||||||
import { of as observableOf } from 'rxjs';
|
import { of as observableOf } from 'rxjs';
|
||||||
import { ActivatedRouteStub } from '../../shared/testing/active-router.stub';
|
import { ActivatedRouteStub } from '../../shared/testing/active-router.stub';
|
||||||
import { Community } from '../../core/shared/community.model';
|
import { Community } from '../../core/shared/community.model';
|
||||||
@@ -24,6 +24,8 @@ import { APP_CONFIG } from '../../../config/app-config.interface';
|
|||||||
import { environment } from '../../../environments/environment';
|
import { environment } from '../../../environments/environment';
|
||||||
import { SortDirection } from '../../core/cache/models/sort-options.model';
|
import { SortDirection } from '../../core/cache/models/sort-options.model';
|
||||||
import { cold } from 'jasmine-marbles';
|
import { cold } from 'jasmine-marbles';
|
||||||
|
import { Store } from '@ngrx/store';
|
||||||
|
import { BrowseEntry } from '../../core/shared/browse-entry.model';
|
||||||
|
|
||||||
describe('BrowseByDatePageComponent', () => {
|
describe('BrowseByDatePageComponent', () => {
|
||||||
let comp: BrowseByDatePageComponent;
|
let comp: BrowseByDatePageComponent;
|
||||||
@@ -95,7 +97,10 @@ describe('BrowseByDatePageComponent', () => {
|
|||||||
{ provide: Router, useValue: new RouterMock() },
|
{ provide: Router, useValue: new RouterMock() },
|
||||||
{ provide: PaginationService, useValue: paginationService },
|
{ provide: PaginationService, useValue: paginationService },
|
||||||
{ provide: ChangeDetectorRef, useValue: mockCdRef },
|
{ provide: ChangeDetectorRef, useValue: mockCdRef },
|
||||||
{ provide: APP_CONFIG, useValue: environment }
|
{ provide: APP_CONFIG, useValue: environment },
|
||||||
|
{ provide: Store, useValue: {} },
|
||||||
|
{ provide: APP_CONFIG, useValue: environment },
|
||||||
|
{ provide: PLATFORM_ID, useValue: 'browser' },
|
||||||
],
|
],
|
||||||
schemas: [NO_ERRORS_SCHEMA]
|
schemas: [NO_ERRORS_SCHEMA]
|
||||||
}).compileComponents();
|
}).compileComponents();
|
||||||
@@ -131,4 +136,33 @@ describe('BrowseByDatePageComponent', () => {
|
|||||||
//expect(comp.startsWithOptions[0]).toEqual(new Date().getUTCFullYear());
|
//expect(comp.startsWithOptions[0]).toEqual(new Date().getUTCFullYear());
|
||||||
expect(comp.startsWithOptions[0]).toEqual(1960);
|
expect(comp.startsWithOptions[0]).toEqual(1960);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('when rendered in SSR', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
comp.platformId = 'server';
|
||||||
|
spyOn((comp as any).browseService, 'getBrowseItemsFor');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not call getBrowseItemsFor on init', (done) => {
|
||||||
|
comp.ngOnInit();
|
||||||
|
expect((comp as any).browseService.getBrowseItemsFor).not.toHaveBeenCalled();
|
||||||
|
comp.loading$.subscribe((res) => {
|
||||||
|
expect(res).toBeFalsy();
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when rendered in CSR', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
comp.platformId = 'browser';
|
||||||
|
spyOn((comp as any).browseService, 'getBrowseItemsFor').and.returnValue(createSuccessfulRemoteDataObject$(new BrowseEntry()));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call getBrowseItemsFor on init', fakeAsync(() => {
|
||||||
|
comp.ngOnInit();
|
||||||
|
tick(100);
|
||||||
|
expect((comp as any).browseService.getBrowseItemsFor).toHaveBeenCalled();
|
||||||
|
}));
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import { ChangeDetectorRef, Component, Inject } from '@angular/core';
|
import { ChangeDetectorRef, Component, Inject, PLATFORM_ID } from '@angular/core';
|
||||||
import {
|
import {
|
||||||
BrowseByMetadataPageComponent,
|
BrowseByMetadataPageComponent,
|
||||||
browseParamsToOptions
|
browseParamsToOptions
|
||||||
@@ -11,6 +11,7 @@ import { DSpaceObjectDataService } from '../../core/data/dspace-object-data.serv
|
|||||||
import { StartsWithType } from '../../shared/starts-with/starts-with-decorator';
|
import { StartsWithType } from '../../shared/starts-with/starts-with-decorator';
|
||||||
import { PaginationService } from '../../core/pagination/pagination.service';
|
import { PaginationService } from '../../core/pagination/pagination.service';
|
||||||
import { map, take } from 'rxjs/operators';
|
import { map, take } from 'rxjs/operators';
|
||||||
|
import { of as observableOf } from 'rxjs';
|
||||||
import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model';
|
import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model';
|
||||||
import { SortDirection, SortOptions } from '../../core/cache/models/sort-options.model';
|
import { SortDirection, SortOptions } from '../../core/cache/models/sort-options.model';
|
||||||
import { isValidDate } from '../../shared/date.util';
|
import { isValidDate } from '../../shared/date.util';
|
||||||
@@ -18,6 +19,8 @@ import { APP_CONFIG, AppConfig } from '../../../config/app-config.interface';
|
|||||||
import { RemoteData } from '../../core/data/remote-data';
|
import { RemoteData } from '../../core/data/remote-data';
|
||||||
import { Item } from '../../core/shared/item.model';
|
import { Item } from '../../core/shared/item.model';
|
||||||
import { DSONameService } from '../../core/breadcrumbs/dso-name.service';
|
import { DSONameService } from '../../core/breadcrumbs/dso-name.service';
|
||||||
|
import { isPlatformServer } from '@angular/common';
|
||||||
|
import { environment } from '../../../environments/environment';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-browse-by-date-page',
|
selector: 'ds-browse-by-date-page',
|
||||||
@@ -44,11 +47,16 @@ export class BrowseByDatePageComponent extends BrowseByMetadataPageComponent {
|
|||||||
protected cdRef: ChangeDetectorRef,
|
protected cdRef: ChangeDetectorRef,
|
||||||
@Inject(APP_CONFIG) public appConfig: AppConfig,
|
@Inject(APP_CONFIG) public appConfig: AppConfig,
|
||||||
public dsoNameService: DSONameService,
|
public dsoNameService: DSONameService,
|
||||||
|
@Inject(PLATFORM_ID) public platformId: any,
|
||||||
) {
|
) {
|
||||||
super(route, browseService, dsoService, paginationService, router, appConfig, dsoNameService);
|
super(route, browseService, dsoService, paginationService, router, appConfig, dsoNameService, platformId);
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
|
if (!this.renderOnServerSide && !environment.universal.enableBrowseComponent && isPlatformServer(this.platformId)) {
|
||||||
|
this.loading$ = observableOf(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
const sortConfig = new SortOptions('default', SortDirection.ASC);
|
const sortConfig = new SortOptions('default', SortDirection.ASC);
|
||||||
this.startsWithType = StartsWithType.date;
|
this.startsWithType = StartsWithType.date;
|
||||||
this.currentPagination$ = this.paginationService.getCurrentPagination(this.paginationConfig.id, this.paginationConfig);
|
this.currentPagination$ = this.paginationService.getCurrentPagination(this.paginationConfig.id, this.paginationConfig);
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
<div class="container">
|
<div class="container" *ngIf="(!ssrRenderingDisabled)">
|
||||||
<ng-container *ngVar="(parent$ | async) as parent">
|
<ng-container *ngVar="(parent$ | async) as parent">
|
||||||
<ng-container *ngIf="parent?.payload as parentContext">
|
<ng-container *ngIf="parent?.payload as parentContext">
|
||||||
<div class="d-flex flex-row border-bottom mb-4 pb-4">
|
<div class="d-flex flex-row border-bottom mb-4 pb-4">
|
||||||
|
@@ -3,7 +3,7 @@ import {
|
|||||||
browseParamsToOptions,
|
browseParamsToOptions,
|
||||||
getBrowseSearchOptions
|
getBrowseSearchOptions
|
||||||
} from './browse-by-metadata-page.component';
|
} from './browse-by-metadata-page.component';
|
||||||
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
import { ComponentFixture, fakeAsync, TestBed, tick, waitForAsync } from '@angular/core/testing';
|
||||||
import { BrowseService } from '../../core/browse/browse.service';
|
import { BrowseService } from '../../core/browse/browse.service';
|
||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
import { RouterTestingModule } from '@angular/router/testing';
|
import { RouterTestingModule } from '@angular/router/testing';
|
||||||
@@ -13,7 +13,7 @@ import { EnumKeysPipe } from '../../shared/utils/enum-keys-pipe';
|
|||||||
import { ActivatedRoute, Router } from '@angular/router';
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
import { ActivatedRouteStub } from '../../shared/testing/active-router.stub';
|
import { ActivatedRouteStub } from '../../shared/testing/active-router.stub';
|
||||||
import { Observable, of as observableOf } from 'rxjs';
|
import { Observable, of as observableOf } from 'rxjs';
|
||||||
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
import { NO_ERRORS_SCHEMA, PLATFORM_ID } from '@angular/core';
|
||||||
import { RemoteData } from '../../core/data/remote-data';
|
import { RemoteData } from '../../core/data/remote-data';
|
||||||
import { buildPaginatedList, PaginatedList } from '../../core/data/paginated-list.model';
|
import { buildPaginatedList, PaginatedList } from '../../core/data/paginated-list.model';
|
||||||
import { PageInfo } from '../../core/shared/page-info.model';
|
import { PageInfo } from '../../core/shared/page-info.model';
|
||||||
@@ -111,7 +111,8 @@ describe('BrowseByMetadataPageComponent', () => {
|
|||||||
{ provide: DSpaceObjectDataService, useValue: mockDsoService },
|
{ provide: DSpaceObjectDataService, useValue: mockDsoService },
|
||||||
{ provide: PaginationService, useValue: paginationService },
|
{ provide: PaginationService, useValue: paginationService },
|
||||||
{ provide: Router, useValue: new RouterMock() },
|
{ provide: Router, useValue: new RouterMock() },
|
||||||
{ provide: APP_CONFIG, useValue: environmentMock }
|
{ provide: APP_CONFIG, useValue: environmentMock },
|
||||||
|
{ provide: PLATFORM_ID, useValue: 'browser' },
|
||||||
],
|
],
|
||||||
schemas: [NO_ERRORS_SCHEMA]
|
schemas: [NO_ERRORS_SCHEMA]
|
||||||
}).compileComponents();
|
}).compileComponents();
|
||||||
@@ -224,6 +225,35 @@ describe('BrowseByMetadataPageComponent', () => {
|
|||||||
expect(result.fetchThumbnail).toBeTrue();
|
expect(result.fetchThumbnail).toBeTrue();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('when rendered in SSR', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
comp.ssrRenderingDisabled = true;
|
||||||
|
spyOn((comp as any).browseService, 'getBrowseEntriesFor').and.returnValue(createSuccessfulRemoteDataObject$(null));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not call getBrowseEntriesFor on init', (done) => {
|
||||||
|
comp.ngOnInit();
|
||||||
|
expect((comp as any).browseService.getBrowseEntriesFor).not.toHaveBeenCalled();
|
||||||
|
comp.loading$.subscribe((res) => {
|
||||||
|
expect(res).toBeFalsy();
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when rendered in CSR', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
comp.ssrRenderingDisabled = false;
|
||||||
|
spyOn((comp as any).browseService, 'getBrowseEntriesFor').and.returnValue(createSuccessfulRemoteDataObject$(new BrowseEntry()));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call getBrowseEntriesFor on init', fakeAsync(() => {
|
||||||
|
comp.ngOnInit();
|
||||||
|
tick(100);
|
||||||
|
expect((comp as any).browseService.getBrowseEntriesFor).toHaveBeenCalled();
|
||||||
|
}));
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
export function toRemoteData(objects: any[]): Observable<RemoteData<PaginatedList<any>>> {
|
export function toRemoteData(objects: any[]): Observable<RemoteData<PaginatedList<any>>> {
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import { combineLatest as observableCombineLatest, Observable, Subscription, of as observableOf } from 'rxjs';
|
import { combineLatest as observableCombineLatest, Observable, Subscription, of as observableOf } from 'rxjs';
|
||||||
import { Component, Inject, OnInit, OnDestroy } from '@angular/core';
|
import { Component, Inject, OnInit, OnDestroy, Input, PLATFORM_ID } from '@angular/core';
|
||||||
import { RemoteData } from '../../core/data/remote-data';
|
import { RemoteData } from '../../core/data/remote-data';
|
||||||
import { PaginatedList } from '../../core/data/paginated-list.model';
|
import { PaginatedList } from '../../core/data/paginated-list.model';
|
||||||
import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model';
|
import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model';
|
||||||
@@ -22,6 +22,8 @@ import { Collection } from '../../core/shared/collection.model';
|
|||||||
import { Community } from '../../core/shared/community.model';
|
import { Community } from '../../core/shared/community.model';
|
||||||
import { APP_CONFIG, AppConfig } from '../../../config/app-config.interface';
|
import { APP_CONFIG, AppConfig } from '../../../config/app-config.interface';
|
||||||
import { DSONameService } from '../../core/breadcrumbs/dso-name.service';
|
import { DSONameService } from '../../core/breadcrumbs/dso-name.service';
|
||||||
|
import { isPlatformServer } from '@angular/common';
|
||||||
|
import { environment } from '../../../environments/environment';
|
||||||
|
|
||||||
export const BBM_PAGINATION_ID = 'bbm';
|
export const BBM_PAGINATION_ID = 'bbm';
|
||||||
|
|
||||||
@@ -37,7 +39,10 @@ export const BBM_PAGINATION_ID = 'bbm';
|
|||||||
* 'dc.contributor.*'
|
* 'dc.contributor.*'
|
||||||
*/
|
*/
|
||||||
export class BrowseByMetadataPageComponent implements OnInit, OnDestroy {
|
export class BrowseByMetadataPageComponent implements OnInit, OnDestroy {
|
||||||
|
/**
|
||||||
|
* Defines whether to fetch search results during SSR execution
|
||||||
|
*/
|
||||||
|
@Input() renderOnServerSide = false;
|
||||||
/**
|
/**
|
||||||
* The list of browse-entries to display
|
* The list of browse-entries to display
|
||||||
*/
|
*/
|
||||||
@@ -126,6 +131,10 @@ export class BrowseByMetadataPageComponent implements OnInit, OnDestroy {
|
|||||||
* Observable determining if the loading animation needs to be shown
|
* Observable determining if the loading animation needs to be shown
|
||||||
*/
|
*/
|
||||||
loading$ = observableOf(true);
|
loading$ = observableOf(true);
|
||||||
|
/**
|
||||||
|
* Whether this component should be rendered or not in SSR
|
||||||
|
*/
|
||||||
|
ssrRenderingDisabled = false;
|
||||||
|
|
||||||
public constructor(protected route: ActivatedRoute,
|
public constructor(protected route: ActivatedRoute,
|
||||||
protected browseService: BrowseService,
|
protected browseService: BrowseService,
|
||||||
@@ -134,6 +143,7 @@ export class BrowseByMetadataPageComponent implements OnInit, OnDestroy {
|
|||||||
protected router: Router,
|
protected router: Router,
|
||||||
@Inject(APP_CONFIG) public appConfig: AppConfig,
|
@Inject(APP_CONFIG) public appConfig: AppConfig,
|
||||||
public dsoNameService: DSONameService,
|
public dsoNameService: DSONameService,
|
||||||
|
@Inject(PLATFORM_ID) public platformId: any,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
this.fetchThumbnails = this.appConfig.browseBy.showThumbnails;
|
this.fetchThumbnails = this.appConfig.browseBy.showThumbnails;
|
||||||
@@ -142,11 +152,15 @@ export class BrowseByMetadataPageComponent implements OnInit, OnDestroy {
|
|||||||
currentPage: 1,
|
currentPage: 1,
|
||||||
pageSize: this.appConfig.browseBy.pageSize,
|
pageSize: this.appConfig.browseBy.pageSize,
|
||||||
});
|
});
|
||||||
|
this.ssrRenderingDisabled = !this.renderOnServerSide && !environment.universal.enableBrowseComponent && isPlatformServer(this.platformId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
|
if (this.ssrRenderingDisabled) {
|
||||||
|
this.loading$ = observableOf(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
const sortConfig = new SortOptions('default', SortDirection.ASC);
|
const sortConfig = new SortOptions('default', SortDirection.ASC);
|
||||||
this.updatePage(getBrowseSearchOptions(this.defaultBrowseId, this.paginationConfig, sortConfig));
|
this.updatePage(getBrowseSearchOptions(this.defaultBrowseId, this.paginationConfig, sortConfig));
|
||||||
this.currentPagination$ = this.paginationService.getCurrentPagination(this.paginationConfig.id, this.paginationConfig);
|
this.currentPagination$ = this.paginationService.getCurrentPagination(this.paginationConfig.id, this.paginationConfig);
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
import { ComponentFixture, fakeAsync, TestBed, tick, waitForAsync } from '@angular/core/testing';
|
||||||
import { ActivatedRoute, Router } from '@angular/router';
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
import { Item } from '../../core/shared/item.model';
|
import { Item } from '../../core/shared/item.model';
|
||||||
import { ActivatedRouteStub } from '../../shared/testing/active-router.stub';
|
import { ActivatedRouteStub } from '../../shared/testing/active-router.stub';
|
||||||
@@ -22,6 +22,7 @@ import { PaginationService } from '../../core/pagination/pagination.service';
|
|||||||
import { PaginationServiceStub } from '../../shared/testing/pagination-service.stub';
|
import { PaginationServiceStub } from '../../shared/testing/pagination-service.stub';
|
||||||
import { APP_CONFIG } from '../../../config/app-config.interface';
|
import { APP_CONFIG } from '../../../config/app-config.interface';
|
||||||
import { environment } from '../../../environments/environment';
|
import { environment } from '../../../environments/environment';
|
||||||
|
import { BrowseEntry } from '../../core/shared/browse-entry.model';
|
||||||
|
|
||||||
|
|
||||||
describe('BrowseByTitlePageComponent', () => {
|
describe('BrowseByTitlePageComponent', () => {
|
||||||
@@ -63,7 +64,8 @@ describe('BrowseByTitlePageComponent', () => {
|
|||||||
|
|
||||||
const activatedRouteStub = Object.assign(new ActivatedRouteStub(), {
|
const activatedRouteStub = Object.assign(new ActivatedRouteStub(), {
|
||||||
params: observableOf({}),
|
params: observableOf({}),
|
||||||
data: observableOf({ metadata: 'title' })
|
queryParams: observableOf({}),
|
||||||
|
data: observableOf({ metadata: 'title' }),
|
||||||
});
|
});
|
||||||
|
|
||||||
const paginationService = new PaginationServiceStub();
|
const paginationService = new PaginationServiceStub();
|
||||||
@@ -97,4 +99,35 @@ describe('BrowseByTitlePageComponent', () => {
|
|||||||
expect(result.payload.page).toEqual(mockItems);
|
expect(result.payload.page).toEqual(mockItems);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('when rendered in SSR', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
comp.platformId = 'server';
|
||||||
|
spyOn((comp as any).browseService, 'getBrowseItemsFor');
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not call getBrowseItemsFor on init', (done) => {
|
||||||
|
comp.ngOnInit();
|
||||||
|
expect((comp as any).browseService.getBrowseItemsFor).not.toHaveBeenCalled();
|
||||||
|
comp.loading$.subscribe((res) => {
|
||||||
|
expect(res).toBeFalsy();
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when rendered in CSR', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
comp.platformId = 'browser';
|
||||||
|
fixture.detectChanges();
|
||||||
|
spyOn((comp as any).browseService, 'getBrowseItemsFor').and.returnValue(createSuccessfulRemoteDataObject$(new BrowseEntry()));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call getBrowseItemsFor on init', fakeAsync(() => {
|
||||||
|
comp.ngOnInit();
|
||||||
|
tick(100);
|
||||||
|
expect((comp as any).browseService.getBrowseItemsFor).toHaveBeenCalled();
|
||||||
|
}));
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import { combineLatest as observableCombineLatest } from 'rxjs';
|
import { combineLatest as observableCombineLatest } from 'rxjs';
|
||||||
import { Component, Inject } from '@angular/core';
|
import { Component, Inject, PLATFORM_ID } from '@angular/core';
|
||||||
import { ActivatedRoute, Params, Router } from '@angular/router';
|
import { ActivatedRoute, Params, Router } from '@angular/router';
|
||||||
import { hasValue } from '../../shared/empty.util';
|
import { hasValue } from '../../shared/empty.util';
|
||||||
import {
|
import {
|
||||||
@@ -11,9 +11,12 @@ import { BrowseService } from '../../core/browse/browse.service';
|
|||||||
import { SortDirection, SortOptions } from '../../core/cache/models/sort-options.model';
|
import { SortDirection, SortOptions } from '../../core/cache/models/sort-options.model';
|
||||||
import { PaginationService } from '../../core/pagination/pagination.service';
|
import { PaginationService } from '../../core/pagination/pagination.service';
|
||||||
import { map, take } from 'rxjs/operators';
|
import { map, take } from 'rxjs/operators';
|
||||||
|
import { of as observableOf } from 'rxjs';
|
||||||
import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model';
|
import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model';
|
||||||
import { AppConfig, APP_CONFIG } from '../../../config/app-config.interface';
|
import { AppConfig, APP_CONFIG } from '../../../config/app-config.interface';
|
||||||
import { DSONameService } from '../../core/breadcrumbs/dso-name.service';
|
import { DSONameService } from '../../core/breadcrumbs/dso-name.service';
|
||||||
|
import { isPlatformServer } from '@angular/common';
|
||||||
|
import { environment } from '../../../environments/environment';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-browse-by-title-page',
|
selector: 'ds-browse-by-title-page',
|
||||||
@@ -32,11 +35,16 @@ export class BrowseByTitlePageComponent extends BrowseByMetadataPageComponent {
|
|||||||
protected router: Router,
|
protected router: Router,
|
||||||
@Inject(APP_CONFIG) public appConfig: AppConfig,
|
@Inject(APP_CONFIG) public appConfig: AppConfig,
|
||||||
public dsoNameService: DSONameService,
|
public dsoNameService: DSONameService,
|
||||||
|
@Inject(PLATFORM_ID) public platformId: any,
|
||||||
) {
|
) {
|
||||||
super(route, browseService, dsoService, paginationService, router, appConfig, dsoNameService);
|
super(route, browseService, dsoService, paginationService, router, appConfig, dsoNameService, platformId);
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
|
if (!this.renderOnServerSide && !environment.universal.enableBrowseComponent && isPlatformServer(this.platformId)) {
|
||||||
|
this.loading$ = observableOf(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
const sortConfig = new SortOptions('dc.title', SortDirection.ASC);
|
const sortConfig = new SortOptions('dc.title', SortDirection.ASC);
|
||||||
this.currentPagination$ = this.paginationService.getCurrentPagination(this.paginationConfig.id, this.paginationConfig);
|
this.currentPagination$ = this.paginationService.getCurrentPagination(this.paginationConfig.id, this.paginationConfig);
|
||||||
this.currentSort$ = this.paginationService.getCurrentSort(this.paginationConfig.id, sortConfig);
|
this.currentSort$ = this.paginationService.getCurrentSort(this.paginationConfig.id, sortConfig);
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
import { HostWindowService } from '../shared/host-window.service';
|
import { HostWindowService } from '../shared/host-window.service';
|
||||||
import { SidebarService } from '../shared/sidebar/sidebar.service';
|
import { SidebarService } from '../shared/sidebar/sidebar.service';
|
||||||
import { SearchComponent } from '../shared/search/search.component';
|
import { SearchComponent } from '../shared/search/search.component';
|
||||||
import { ChangeDetectionStrategy, Component, Inject } from '@angular/core';
|
import { ChangeDetectionStrategy, Component, Inject, PLATFORM_ID } from '@angular/core';
|
||||||
import { pushInOut } from '../shared/animations/push';
|
import { pushInOut } from '../shared/animations/push';
|
||||||
import { SEARCH_CONFIG_SERVICE } from '../my-dspace-page/my-dspace-page.component';
|
import { SEARCH_CONFIG_SERVICE } from '../my-dspace-page/my-dspace-page.component';
|
||||||
import { SearchConfigurationService } from '../core/shared/search/search-configuration.service';
|
import { SearchConfigurationService } from '../core/shared/search/search-configuration.service';
|
||||||
@@ -35,7 +35,8 @@ export class ConfigurationSearchPageComponent extends SearchComponent {
|
|||||||
protected routeService: RouteService,
|
protected routeService: RouteService,
|
||||||
protected router: Router,
|
protected router: Router,
|
||||||
@Inject(APP_CONFIG) protected appConfig: AppConfig,
|
@Inject(APP_CONFIG) protected appConfig: AppConfig,
|
||||||
|
@Inject(PLATFORM_ID) public platformId: any,
|
||||||
) {
|
) {
|
||||||
super(service, sidebarService, windowService, searchConfigService, routeService, router, appConfig);
|
super(service, sidebarService, windowService, searchConfigService, routeService, router, appConfig, platformId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import { Component, Inject, Input, OnInit } from '@angular/core';
|
import { Component, EventEmitter, Inject, Input, OnInit, Output } from '@angular/core';
|
||||||
|
|
||||||
import { BehaviorSubject, Observable, of as observableOf } from 'rxjs';
|
import { BehaviorSubject, Observable, of as observableOf } from 'rxjs';
|
||||||
import { filter, map, startWith, switchMap, take } from 'rxjs/operators';
|
import { filter, map, startWith, switchMap, take } from 'rxjs/operators';
|
||||||
@@ -43,6 +43,8 @@ export class SearchFilterComponent implements OnInit {
|
|||||||
*/
|
*/
|
||||||
@Input() scope: string;
|
@Input() scope: string;
|
||||||
|
|
||||||
|
@Output() isVisibilityComputed = new EventEmitter<boolean>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* True when the filter is 100% collapsed in the UI
|
* True when the filter is 100% collapsed in the UI
|
||||||
*/
|
*/
|
||||||
@@ -91,7 +93,9 @@ export class SearchFilterComponent implements OnInit {
|
|||||||
*/
|
*/
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.selectedValues$ = this.getSelectedValues();
|
this.selectedValues$ = this.getSelectedValues();
|
||||||
this.active$ = this.isActive();
|
this.active$ = this.isActive().pipe(
|
||||||
|
startWith(true)
|
||||||
|
);
|
||||||
this.collapsed$ = this.isCollapsed();
|
this.collapsed$ = this.isCollapsed();
|
||||||
this.initializeFilter();
|
this.initializeFilter();
|
||||||
this.selectedValues$.pipe(take(1)).subscribe((selectedValues) => {
|
this.selectedValues$.pipe(take(1)).subscribe((selectedValues) => {
|
||||||
@@ -99,6 +103,9 @@ export class SearchFilterComponent implements OnInit {
|
|||||||
this.filterService.expand(this.filter.name);
|
this.filterService.expand(this.filter.name);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
this.isActive().pipe(take(1)).subscribe(() => {
|
||||||
|
this.isVisibilityComputed.emit(true);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -187,7 +194,7 @@ export class SearchFilterComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}),
|
})
|
||||||
startWith(true));
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,7 +1,13 @@
|
|||||||
<h3>{{"search.filters.head" | translate}}</h3>
|
<h3>{{"search.filters.head" | translate}}</h3>
|
||||||
<div *ngIf="(filters | async)?.hasSucceeded">
|
<div *ngIf="(filters | async)?.hasSucceeded">
|
||||||
<div *ngFor="let filter of (filters | async)?.payload; trackBy: trackUpdate">
|
<div [class.visually-hidden]="filtersWithComputedVisibility !== (filters | async)?.payload?.length"
|
||||||
<ds-search-filter [scope]="currentScope" [filter]="filter" [inPlaceSearch]="inPlaceSearch" [refreshFilters]="refreshFilters"></ds-search-filter>
|
*ngFor="let filter of (filters | async)?.payload; trackBy: trackUpdate">
|
||||||
|
<ds-search-filter (isVisibilityComputed)="countFiltersWithComputedVisibility($event)" [scope]="currentScope" [filter]="filter" [inPlaceSearch]="inPlaceSearch" [refreshFilters]="refreshFilters"></ds-search-filter>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<ng-container *ngIf="filtersWithComputedVisibility !== (filters | async)?.payload?.length">
|
||||||
|
<ngx-skeleton-loader [count]="defaultFilterCount"/>
|
||||||
|
</ng-container>
|
||||||
<a class="btn btn-primary" [routerLink]="[searchLink]" [queryParams]="clearParams | async" queryParamsHandling="merge" role="button"><i class="fas fa-undo"></i> {{"search.filters.reset" | translate}}</a>
|
<a class="btn btn-primary" [routerLink]="[searchLink]" [queryParams]="clearParams | async" queryParamsHandling="merge" role="button"><i class="fas fa-undo"></i> {{"search.filters.reset" | translate}}</a>
|
||||||
|
@@ -1,2 +1,12 @@
|
|||||||
@import '../../../../styles/variables';
|
@import '../../../../styles/variables';
|
||||||
@import '../../../../styles/mixins';
|
@import '../../../../styles/mixins';
|
||||||
|
|
||||||
|
:host ::ng-deep {
|
||||||
|
ngx-skeleton-loader .skeleton-loader {
|
||||||
|
height: var(--ds-filters-skeleton-height);
|
||||||
|
margin-bottom: var(--ds-filters-skeleton-spacing);
|
||||||
|
padding: var(--ds-filters-skeleton-spacing);
|
||||||
|
background-color: var(--bs-light);
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -9,6 +9,8 @@ import { SearchFiltersComponent } from './search-filters.component';
|
|||||||
import { SearchService } from '../../../core/shared/search/search.service';
|
import { SearchService } from '../../../core/shared/search/search.service';
|
||||||
import { SEARCH_CONFIG_SERVICE } from '../../../my-dspace-page/my-dspace-page.component';
|
import { SEARCH_CONFIG_SERVICE } from '../../../my-dspace-page/my-dspace-page.component';
|
||||||
import { SearchConfigurationServiceStub } from '../../testing/search-configuration-service.stub';
|
import { SearchConfigurationServiceStub } from '../../testing/search-configuration-service.stub';
|
||||||
|
import { APP_CONFIG } from '../../../../config/app-config.interface';
|
||||||
|
import { environment } from '../../../../environments/environment';
|
||||||
|
|
||||||
describe('SearchFiltersComponent', () => {
|
describe('SearchFiltersComponent', () => {
|
||||||
let comp: SearchFiltersComponent;
|
let comp: SearchFiltersComponent;
|
||||||
@@ -37,7 +39,7 @@ describe('SearchFiltersComponent', () => {
|
|||||||
{ provide: SearchService, useValue: searchServiceStub },
|
{ provide: SearchService, useValue: searchServiceStub },
|
||||||
{ provide: SEARCH_CONFIG_SERVICE, useValue: new SearchConfigurationServiceStub() },
|
{ provide: SEARCH_CONFIG_SERVICE, useValue: new SearchConfigurationServiceStub() },
|
||||||
{ provide: SearchFilterService, useValue: searchFiltersStub },
|
{ provide: SearchFilterService, useValue: searchFiltersStub },
|
||||||
|
{ provide: APP_CONFIG, useValue: environment },
|
||||||
],
|
],
|
||||||
schemas: [NO_ERRORS_SCHEMA]
|
schemas: [NO_ERRORS_SCHEMA]
|
||||||
}).overrideComponent(SearchFiltersComponent, {
|
}).overrideComponent(SearchFiltersComponent, {
|
||||||
|
@@ -2,7 +2,7 @@ import { Component, Inject, Input, OnDestroy, OnInit } from '@angular/core';
|
|||||||
import { Router } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
|
|
||||||
import { BehaviorSubject, Observable } from 'rxjs';
|
import { BehaviorSubject, Observable } from 'rxjs';
|
||||||
import { map } from 'rxjs/operators';
|
import { map, tap } from 'rxjs/operators';
|
||||||
|
|
||||||
import { SearchService } from '../../../core/shared/search/search.service';
|
import { SearchService } from '../../../core/shared/search/search.service';
|
||||||
import { RemoteData } from '../../../core/data/remote-data';
|
import { RemoteData } from '../../../core/data/remote-data';
|
||||||
@@ -12,6 +12,7 @@ import { SearchFilterService } from '../../../core/shared/search/search-filter.s
|
|||||||
import { SEARCH_CONFIG_SERVICE } from '../../../my-dspace-page/my-dspace-page.component';
|
import { SEARCH_CONFIG_SERVICE } from '../../../my-dspace-page/my-dspace-page.component';
|
||||||
import { currentPath } from '../../utils/route.utils';
|
import { currentPath } from '../../utils/route.utils';
|
||||||
import { hasValue } from '../../empty.util';
|
import { hasValue } from '../../empty.util';
|
||||||
|
import { APP_CONFIG, AppConfig } from '../../../../config/app-config.interface';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-search-filters',
|
selector: 'ds-search-filters',
|
||||||
@@ -60,7 +61,13 @@ export class SearchFiltersComponent implements OnInit, OnDestroy {
|
|||||||
*/
|
*/
|
||||||
searchLink: string;
|
searchLink: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filters for which visibility has been computed
|
||||||
|
*/
|
||||||
|
filtersWithComputedVisibility = 0;
|
||||||
|
|
||||||
subs = [];
|
subs = [];
|
||||||
|
defaultFilterCount: number;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize instance variables
|
* Initialize instance variables
|
||||||
@@ -68,19 +75,26 @@ export class SearchFiltersComponent implements OnInit, OnDestroy {
|
|||||||
* @param {SearchFilterService} filterService
|
* @param {SearchFilterService} filterService
|
||||||
* @param {Router} router
|
* @param {Router} router
|
||||||
* @param {SearchConfigurationService} searchConfigService
|
* @param {SearchConfigurationService} searchConfigService
|
||||||
|
* @param appConfig
|
||||||
*/
|
*/
|
||||||
constructor(
|
constructor(
|
||||||
private searchService: SearchService,
|
private searchService: SearchService,
|
||||||
private filterService: SearchFilterService,
|
private filterService: SearchFilterService,
|
||||||
private router: Router,
|
private router: Router,
|
||||||
@Inject(SEARCH_CONFIG_SERVICE) private searchConfigService: SearchConfigurationService) {
|
@Inject(SEARCH_CONFIG_SERVICE) private searchConfigService: SearchConfigurationService,
|
||||||
|
@Inject(APP_CONFIG) protected appConfig: AppConfig,
|
||||||
|
) {
|
||||||
|
this.defaultFilterCount = this.appConfig.search.filterPlaceholdersCount ?? 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.clearParams = this.searchConfigService.getCurrentFrontendFilters().pipe(map((filters) => {
|
this.clearParams = this.searchConfigService.getCurrentFrontendFilters().pipe(
|
||||||
|
tap(() => this.filtersWithComputedVisibility = 0),
|
||||||
|
map((filters) => {
|
||||||
Object.keys(filters).forEach((f) => filters[f] = null);
|
Object.keys(filters).forEach((f) => filters[f] = null);
|
||||||
return filters;
|
return filters;
|
||||||
}));
|
})
|
||||||
|
);
|
||||||
this.searchLink = this.getSearchLink();
|
this.searchLink = this.getSearchLink();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,4 +122,10 @@ export class SearchFiltersComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
countFiltersWithComputedVisibility(computed: boolean) {
|
||||||
|
if (computed) {
|
||||||
|
this.filtersWithComputedVisibility += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,38 @@
|
|||||||
|
<div class="row flex-nowrap">
|
||||||
|
<div [class.mb-2]="(viewMode$ | async) === ViewMode.ListElement" class="info-skeleton col-12">
|
||||||
|
<ngx-skeleton-loader/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ng-container *ngIf="(viewMode$ | async) === ViewMode.ListElement; else grid">
|
||||||
|
<ng-container *ngFor="let result of loadingResults; let first = first">
|
||||||
|
<div [class.my-4]="!first" class="row">
|
||||||
|
<div *ngIf="showThumbnails" class="col-3 col-md-2">
|
||||||
|
<div class="thumbnail-skeleton position-relative">
|
||||||
|
<ngx-skeleton-loader/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div [class.col-9]="showThumbnails" [class.col-md-10]="showThumbnails" [class.col-md-12]="!showThumbnails">
|
||||||
|
<div class="badge-skeleton">
|
||||||
|
<ngx-skeleton-loader/>
|
||||||
|
</div>
|
||||||
|
<div class="text-skeleton">
|
||||||
|
<ngx-skeleton-loader [count]="textLineCount"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ng-container>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-template #grid>
|
||||||
|
<div class="card-columns row">
|
||||||
|
<ng-container *ngFor="let result of loadingResults">
|
||||||
|
<div class="card-column col col-sm-6 col-lg-4">
|
||||||
|
<div class="card-skeleton">
|
||||||
|
<ngx-skeleton-loader/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ng-container>
|
||||||
|
</div>
|
||||||
|
</ng-template>
|
||||||
|
|
@@ -0,0 +1,56 @@
|
|||||||
|
:host ::ng-deep {
|
||||||
|
--ds-wrapper-grid-spacing: calc(var(--bs-spacer) / 2);
|
||||||
|
|
||||||
|
.info-skeleton, .badge-skeleton, .text-skeleton{
|
||||||
|
ngx-skeleton-loader .skeleton-loader {
|
||||||
|
height: var(--ds-search-skeleton-text-height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge-skeleton, .info-skeleton {
|
||||||
|
ngx-skeleton-loader .skeleton-loader {
|
||||||
|
width: var(--ds-search-skeleton-badge-width);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-skeleton {
|
||||||
|
ngx-skeleton-loader .skeleton-loader {
|
||||||
|
width: var(--ds-search-skeleton-info-width);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.thumbnail-skeleton {
|
||||||
|
max-width: var(--ds-thumbnail-max-width);
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
ngx-skeleton-loader .skeleton-loader {
|
||||||
|
margin-right: var(--ds-search-skeleton-thumbnail-margin);
|
||||||
|
border-radius: 0;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-skeleton {
|
||||||
|
ngx-skeleton-loader .skeleton-loader {
|
||||||
|
height: var(--ds-search-skeleton-card-height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ngx-skeleton-loader .skeleton-loader {
|
||||||
|
background-color: var(--bs-light);
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-columns {
|
||||||
|
margin-left: calc(-1 * var(--ds-wrapper-grid-spacing));
|
||||||
|
margin-right: calc(-1 * var(--ds-wrapper-grid-spacing));
|
||||||
|
column-gap: 0;
|
||||||
|
|
||||||
|
.card-column {
|
||||||
|
padding-left: var(--ds-wrapper-grid-spacing);
|
||||||
|
padding-right: var(--ds-wrapper-grid-spacing);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@@ -0,0 +1,33 @@
|
|||||||
|
import {
|
||||||
|
ComponentFixture,
|
||||||
|
TestBed,
|
||||||
|
} from '@angular/core/testing';
|
||||||
|
import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader';
|
||||||
|
|
||||||
|
import { SearchService } from '../../../../core/shared/search/search.service';
|
||||||
|
import { SearchServiceStub } from '../../../testing/search-service.stub';
|
||||||
|
import { SearchResultsSkeletonComponent } from './search-results-skeleton.component';
|
||||||
|
|
||||||
|
describe('SearchResultsSkeletonComponent', () => {
|
||||||
|
let component: SearchResultsSkeletonComponent;
|
||||||
|
let fixture: ComponentFixture<SearchResultsSkeletonComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [NgxSkeletonLoaderModule],
|
||||||
|
declarations: [SearchResultsSkeletonComponent],
|
||||||
|
providers: [
|
||||||
|
{ provide: SearchService, useValue: new SearchServiceStub() },
|
||||||
|
],
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(SearchResultsSkeletonComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
@@ -0,0 +1,59 @@
|
|||||||
|
import {
|
||||||
|
Component,
|
||||||
|
Input,
|
||||||
|
OnInit,
|
||||||
|
} from '@angular/core';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
|
||||||
|
import { SearchService } from '../../../../core/shared/search/search.service';
|
||||||
|
import { ViewMode } from '../../../../core/shared/view-mode.model';
|
||||||
|
import { hasValue } from '../../../empty.util';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'ds-search-results-skeleton',
|
||||||
|
templateUrl: './search-results-skeleton.component.html',
|
||||||
|
styleUrls: ['./search-results-skeleton.component.scss'],
|
||||||
|
})
|
||||||
|
/**
|
||||||
|
* Component to show placeholders for search results while loading, to give a loading feedback to the user without layout shifting.
|
||||||
|
*/
|
||||||
|
export class SearchResultsSkeletonComponent implements OnInit {
|
||||||
|
/**
|
||||||
|
* Whether the search result contains thumbnail
|
||||||
|
*/
|
||||||
|
@Input()
|
||||||
|
showThumbnails: boolean;
|
||||||
|
/**
|
||||||
|
* The number of search result loaded in the current page
|
||||||
|
*/
|
||||||
|
@Input()
|
||||||
|
numberOfResults = 0;
|
||||||
|
/**
|
||||||
|
* How many placeholder are displayed for the search result text
|
||||||
|
*/
|
||||||
|
@Input()
|
||||||
|
textLineCount = 2;
|
||||||
|
/**
|
||||||
|
* The view mode of the search page
|
||||||
|
*/
|
||||||
|
public viewMode$: Observable<ViewMode>;
|
||||||
|
/**
|
||||||
|
* Array built from numberOfResults to count and iterate based on index
|
||||||
|
*/
|
||||||
|
public loadingResults: number[];
|
||||||
|
|
||||||
|
protected readonly ViewMode = ViewMode;
|
||||||
|
|
||||||
|
constructor(private searchService: SearchService) {
|
||||||
|
this.viewMode$ = this.searchService.getViewMode();
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.loadingResults = Array.from({ length: this.numberOfResults }, (_, i) => i + 1);
|
||||||
|
|
||||||
|
if (!hasValue(this.showThumbnails)) {
|
||||||
|
// this is needed as the default value of show thumbnails is true but set in lower levels of the DOM.
|
||||||
|
this.showThumbnails = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -19,7 +19,13 @@
|
|||||||
(selectObject)="selectObject.emit($event)">
|
(selectObject)="selectObject.emit($event)">
|
||||||
</ds-viewable-collection>
|
</ds-viewable-collection>
|
||||||
</div>
|
</div>
|
||||||
<ds-themed-loading *ngIf="isLoading()" message="{{'loading.search-results' | translate}}"></ds-themed-loading>
|
|
||||||
|
<ds-search-results-skeleton
|
||||||
|
*ngIf="isLoading()"
|
||||||
|
[showThumbnails]="showThumbnails"
|
||||||
|
[numberOfResults]="searchConfig.pagination.pageSize"
|
||||||
|
></ds-search-results-skeleton>
|
||||||
|
|
||||||
<ds-error *ngIf="showError()"
|
<ds-error *ngIf="showError()"
|
||||||
message="{{errorMessageLabel() | translate}}"></ds-error>
|
message="{{errorMessageLabel() | translate}}"></ds-error>
|
||||||
<div *ngIf="searchResults?.payload?.page.length == 0 || searchResults?.statusCode == 400">
|
<div *ngIf="searchResults?.payload?.page.length == 0 || searchResults?.statusCode == 400">
|
||||||
|
@@ -0,0 +1,17 @@
|
|||||||
|
:host ::ng-deep {
|
||||||
|
.filter-badge-skeleton {
|
||||||
|
ngx-skeleton-loader .skeleton-loader {
|
||||||
|
background-color: var(--bs-light);
|
||||||
|
box-shadow: none;
|
||||||
|
width: var(--ds-search-skeleton-filter-badge-width);
|
||||||
|
height: var(--ds-search-skeleton-badge-height);
|
||||||
|
margin-bottom: 0;
|
||||||
|
margin-right: calc(var(--bs-spacer) / 4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.filters-badge-skeleton-container {
|
||||||
|
display: flex;
|
||||||
|
max-height: var(--ds-search-skeleton-badge-height);
|
||||||
|
}
|
||||||
|
}
|
@@ -7,6 +7,11 @@ import { TranslateModule } from '@ngx-translate/core';
|
|||||||
import { SearchResultsComponent } from './search-results.component';
|
import { SearchResultsComponent } from './search-results.component';
|
||||||
import { QueryParamsDirectiveStub } from '../../testing/query-params-directive.stub';
|
import { QueryParamsDirectiveStub } from '../../testing/query-params-directive.stub';
|
||||||
import { createFailedRemoteDataObject } from '../../remote-data.utils';
|
import { createFailedRemoteDataObject } from '../../remote-data.utils';
|
||||||
|
import { SearchResultsSkeletonComponent } from './search-results-skeleton/search-results-skeleton.component';
|
||||||
|
import { SearchConfigurationService } from '../../../core/shared/search/search-configuration.service';
|
||||||
|
import { SearchConfigurationServiceStub } from '../../testing/search-configuration-service.stub';
|
||||||
|
import { SearchService } from '../../../core/shared/search/search.service';
|
||||||
|
import { SearchServiceStub } from '../../testing/search-service.stub';
|
||||||
|
|
||||||
describe('SearchResultsComponent', () => {
|
describe('SearchResultsComponent', () => {
|
||||||
let comp: SearchResultsComponent;
|
let comp: SearchResultsComponent;
|
||||||
@@ -19,7 +24,13 @@ describe('SearchResultsComponent', () => {
|
|||||||
imports: [TranslateModule.forRoot(), NoopAnimationsModule],
|
imports: [TranslateModule.forRoot(), NoopAnimationsModule],
|
||||||
declarations: [
|
declarations: [
|
||||||
SearchResultsComponent,
|
SearchResultsComponent,
|
||||||
QueryParamsDirectiveStub],
|
SearchResultsSkeletonComponent,
|
||||||
|
QueryParamsDirectiveStub
|
||||||
|
],
|
||||||
|
providers: [
|
||||||
|
{ provide: SearchConfigurationService, useValue: new SearchConfigurationServiceStub() },
|
||||||
|
{ provide: SearchService, useValue: new SearchServiceStub() },
|
||||||
|
],
|
||||||
schemas: [NO_ERRORS_SCHEMA]
|
schemas: [NO_ERRORS_SCHEMA]
|
||||||
}).compileComponents();
|
}).compileComponents();
|
||||||
}));
|
}));
|
||||||
@@ -63,7 +74,7 @@ describe('SearchResultsComponent', () => {
|
|||||||
|
|
||||||
it('should display link with new search where query is quoted if search return a error 400', () => {
|
it('should display link with new search where query is quoted if search return a error 400', () => {
|
||||||
(comp as any).searchResults = createFailedRemoteDataObject('Error', 400);
|
(comp as any).searchResults = createFailedRemoteDataObject('Error', 400);
|
||||||
(comp as any).searchConfig = { query: 'foobar' };
|
(comp as any).searchConfig = { query: 'foobar', pagination: { pageSize: 10 } };
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
|
||||||
const linkDes = fixture.debugElement.queryAll(By.directive(QueryParamsDirectiveStub));
|
const linkDes = fixture.debugElement.queryAll(By.directive(QueryParamsDirectiveStub));
|
||||||
|
@@ -11,6 +11,10 @@ import { CollectionElementLinkType } from '../../object-collection/collection-el
|
|||||||
import { ViewMode } from '../../../core/shared/view-mode.model';
|
import { ViewMode } from '../../../core/shared/view-mode.model';
|
||||||
import { Context } from '../../../core/shared/context.model';
|
import { Context } from '../../../core/shared/context.model';
|
||||||
import { PaginatedSearchOptions } from '../models/paginated-search-options.model';
|
import { PaginatedSearchOptions } from '../models/paginated-search-options.model';
|
||||||
|
import { SearchFilter } from '../models/search-filter.model';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
import { SearchConfigurationService } from '../../../core/shared/search/search-configuration.service';
|
||||||
|
import { SearchService } from '../../../core/shared/search/search.service';
|
||||||
|
|
||||||
export interface SelectionConfig {
|
export interface SelectionConfig {
|
||||||
repeatable: boolean;
|
repeatable: boolean;
|
||||||
@@ -19,6 +23,7 @@ export interface SelectionConfig {
|
|||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-search-results',
|
selector: 'ds-search-results',
|
||||||
|
styleUrls: ['./search-results.component.scss'],
|
||||||
templateUrl: './search-results.component.html',
|
templateUrl: './search-results.component.html',
|
||||||
animations: [
|
animations: [
|
||||||
fadeIn,
|
fadeIn,
|
||||||
@@ -32,6 +37,8 @@ export interface SelectionConfig {
|
|||||||
export class SearchResultsComponent {
|
export class SearchResultsComponent {
|
||||||
hasNoValue = hasNoValue;
|
hasNoValue = hasNoValue;
|
||||||
|
|
||||||
|
filters$: Observable<SearchFilter[]>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The link type of the listed search results
|
* The link type of the listed search results
|
||||||
*/
|
*/
|
||||||
@@ -104,6 +111,13 @@ export class SearchResultsComponent {
|
|||||||
|
|
||||||
@Output() selectObject: EventEmitter<ListableObject> = new EventEmitter<ListableObject>();
|
@Output() selectObject: EventEmitter<ListableObject> = new EventEmitter<ListableObject>();
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
protected searchConfigService: SearchConfigurationService,
|
||||||
|
protected searchService: SearchService,
|
||||||
|
) {
|
||||||
|
this.filters$ = this.searchConfigService.getCurrentFilters();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if search results are loading
|
* Check if search results are loading
|
||||||
*/
|
*/
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
|
import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA, PLATFORM_ID } from '@angular/core';
|
||||||
|
|
||||||
import { ComponentFixture, fakeAsync, TestBed, tick, waitForAsync } from '@angular/core/testing';
|
import { ComponentFixture, fakeAsync, TestBed, tick, waitForAsync } from '@angular/core/testing';
|
||||||
import { RouterTestingModule } from '@angular/router/testing';
|
import { RouterTestingModule } from '@angular/router/testing';
|
||||||
@@ -216,6 +216,7 @@ export function configureSearchComponentTestingModule(compType, additionalDeclar
|
|||||||
useValue: searchConfigurationServiceStub
|
useValue: searchConfigurationServiceStub
|
||||||
},
|
},
|
||||||
{ provide: APP_CONFIG, useValue: environment },
|
{ provide: APP_CONFIG, useValue: environment },
|
||||||
|
{ provide: PLATFORM_ID, useValue: 'browser' },
|
||||||
],
|
],
|
||||||
schemas: [NO_ERRORS_SCHEMA]
|
schemas: [NO_ERRORS_SCHEMA]
|
||||||
}).overrideComponent(compType, {
|
}).overrideComponent(compType, {
|
||||||
@@ -374,5 +375,34 @@ describe('SearchComponent', () => {
|
|||||||
expect(result).toBeNull();
|
expect(result).toBeNull();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('when rendered in SSR', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
comp.platformId = 'server';
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not call search method on init', (done) => {
|
||||||
|
comp.ngOnInit();
|
||||||
|
//Check that the first method from which the search depend upon is not being called
|
||||||
|
expect(searchConfigurationServiceStub.getCurrentConfiguration).not.toHaveBeenCalled();
|
||||||
|
comp.initialized$.subscribe((res) => {
|
||||||
|
expect(res).toBeTruthy();
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when rendered in CSR', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
comp.platformId = 'browser';
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call search method on init', fakeAsync(() => {
|
||||||
|
comp.ngOnInit();
|
||||||
|
tick(100);
|
||||||
|
//Check that the last method from which the search depend upon is being called
|
||||||
|
expect(searchServiceStub.search).toHaveBeenCalled();
|
||||||
|
}));
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@@ -1,4 +1,14 @@
|
|||||||
import { ChangeDetectionStrategy, Component, EventEmitter, Inject, Input, OnInit, Output, OnDestroy } from '@angular/core';
|
import {
|
||||||
|
ChangeDetectionStrategy,
|
||||||
|
Component,
|
||||||
|
EventEmitter,
|
||||||
|
Inject,
|
||||||
|
Input,
|
||||||
|
OnInit,
|
||||||
|
Output,
|
||||||
|
OnDestroy,
|
||||||
|
PLATFORM_ID
|
||||||
|
} from '@angular/core';
|
||||||
import { NavigationStart, Router } from '@angular/router';
|
import { NavigationStart, Router } from '@angular/router';
|
||||||
|
|
||||||
import { BehaviorSubject, combineLatest, Observable, Subscription } from 'rxjs';
|
import { BehaviorSubject, combineLatest, Observable, Subscription } from 'rxjs';
|
||||||
@@ -38,6 +48,8 @@ import { ITEM_MODULE_PATH } from '../../item-page/item-page-routing-paths';
|
|||||||
import { COLLECTION_MODULE_PATH } from '../../collection-page/collection-page-routing-paths';
|
import { COLLECTION_MODULE_PATH } from '../../collection-page/collection-page-routing-paths';
|
||||||
import { COMMUNITY_MODULE_PATH } from '../../community-page/community-page-routing-paths';
|
import { COMMUNITY_MODULE_PATH } from '../../community-page/community-page-routing-paths';
|
||||||
import { AppConfig, APP_CONFIG } from '../../../config/app-config.interface';
|
import { AppConfig, APP_CONFIG } from '../../../config/app-config.interface';
|
||||||
|
import { isPlatformServer } from '@angular/common';
|
||||||
|
import { environment } from 'src/environments/environment';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-search',
|
selector: 'ds-search',
|
||||||
@@ -176,6 +188,11 @@ export class SearchComponent implements OnDestroy, OnInit {
|
|||||||
*/
|
*/
|
||||||
@Input() scope: string;
|
@Input() scope: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines whether to fetch search results during SSR execution
|
||||||
|
*/
|
||||||
|
@Input() renderOnServerSide = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The current configuration used during the search
|
* The current configuration used during the search
|
||||||
*/
|
*/
|
||||||
@@ -285,6 +302,7 @@ export class SearchComponent implements OnDestroy, OnInit {
|
|||||||
protected routeService: RouteService,
|
protected routeService: RouteService,
|
||||||
protected router: Router,
|
protected router: Router,
|
||||||
@Inject(APP_CONFIG) protected appConfig: AppConfig,
|
@Inject(APP_CONFIG) protected appConfig: AppConfig,
|
||||||
|
@Inject(PLATFORM_ID) public platformId: any,
|
||||||
) {
|
) {
|
||||||
this.isXsOrSm$ = this.windowService.isXsOrSm();
|
this.isXsOrSm$ = this.windowService.isXsOrSm();
|
||||||
}
|
}
|
||||||
@@ -297,6 +315,14 @@ export class SearchComponent implements OnDestroy, OnInit {
|
|||||||
* If something changes, update the list of scopes for the dropdown
|
* If something changes, update the list of scopes for the dropdown
|
||||||
*/
|
*/
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
|
if (!this.renderOnServerSide && !environment.universal.enableSearchComponent && isPlatformServer(this.platformId)) {
|
||||||
|
this.subs.push(this.getSearchOptions().pipe(distinctUntilChanged()).subscribe((options) => {
|
||||||
|
this.searchOptions$.next(options);
|
||||||
|
}));
|
||||||
|
this.initialized$.next(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (this.useUniquePageId) {
|
if (this.useUniquePageId) {
|
||||||
// Create an unique pagination id related to the instance of the SearchComponent
|
// Create an unique pagination id related to the instance of the SearchComponent
|
||||||
this.paginationId = uniqueId(this.paginationId);
|
this.paginationId = uniqueId(this.paginationId);
|
||||||
|
@@ -34,6 +34,10 @@ import { ThemedSearchSettingsComponent } from './search-settings/themed-search-s
|
|||||||
import { NouisliderModule } from 'ng2-nouislider';
|
import { NouisliderModule } from 'ng2-nouislider';
|
||||||
import { ThemedSearchFiltersComponent } from './search-filters/themed-search-filters.component';
|
import { ThemedSearchFiltersComponent } from './search-filters/themed-search-filters.component';
|
||||||
import { ThemedSearchSidebarComponent } from './search-sidebar/themed-search-sidebar.component';
|
import { ThemedSearchSidebarComponent } from './search-sidebar/themed-search-sidebar.component';
|
||||||
|
import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader';
|
||||||
|
import {
|
||||||
|
SearchResultsSkeletonComponent
|
||||||
|
} from './search-results/search-results-skeleton/search-results-skeleton.component';
|
||||||
|
|
||||||
const COMPONENTS = [
|
const COMPONENTS = [
|
||||||
SearchComponent,
|
SearchComponent,
|
||||||
@@ -62,6 +66,7 @@ const COMPONENTS = [
|
|||||||
ThemedSearchSettingsComponent,
|
ThemedSearchSettingsComponent,
|
||||||
ThemedSearchFiltersComponent,
|
ThemedSearchFiltersComponent,
|
||||||
ThemedSearchSidebarComponent,
|
ThemedSearchSidebarComponent,
|
||||||
|
SearchResultsSkeletonComponent
|
||||||
];
|
];
|
||||||
|
|
||||||
const ENTRY_COMPONENTS = [
|
const ENTRY_COMPONENTS = [
|
||||||
@@ -74,6 +79,7 @@ const ENTRY_COMPONENTS = [
|
|||||||
SearchFacetSelectedOptionComponent,
|
SearchFacetSelectedOptionComponent,
|
||||||
SearchFacetRangeOptionComponent,
|
SearchFacetRangeOptionComponent,
|
||||||
SearchAuthorityFilterComponent,
|
SearchAuthorityFilterComponent,
|
||||||
|
SearchResultsSkeletonComponent
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -93,11 +99,12 @@ export const MODELS = [
|
|||||||
imports: [
|
imports: [
|
||||||
CommonModule,
|
CommonModule,
|
||||||
TranslateModule.forChild({
|
TranslateModule.forChild({
|
||||||
missingTranslationHandler: { provide: MissingTranslationHandler, useClass: MissingTranslationHelper },
|
missingTranslationHandler: {provide: MissingTranslationHandler, useClass: MissingTranslationHelper},
|
||||||
useDefaultLang: true
|
useDefaultLang: true
|
||||||
}),
|
}),
|
||||||
SharedModule.withEntryComponents(),
|
SharedModule.withEntryComponents(),
|
||||||
NouisliderModule,
|
NouisliderModule,
|
||||||
|
NgxSkeletonLoaderModule,
|
||||||
],
|
],
|
||||||
exports: [
|
exports: [
|
||||||
...COMPONENTS
|
...COMPONENTS
|
||||||
|
@@ -13,6 +13,10 @@ export class SearchConfigurationServiceStub {
|
|||||||
return observableOf([]);
|
return observableOf([]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getCurrentFilters() {
|
||||||
|
return observableOf([]);
|
||||||
|
}
|
||||||
|
|
||||||
getCurrentScope(a) {
|
getCurrentScope(a) {
|
||||||
return observableOf('test-id');
|
return observableOf('test-id');
|
||||||
}
|
}
|
||||||
|
@@ -23,6 +23,7 @@ import { MarkdownConfig } from './markdown-config.interface';
|
|||||||
import { FilterVocabularyConfig } from './filter-vocabulary-config';
|
import { FilterVocabularyConfig } from './filter-vocabulary-config';
|
||||||
import { DiscoverySortConfig } from './discovery-sort.config';
|
import { DiscoverySortConfig } from './discovery-sort.config';
|
||||||
import { LiveRegionConfig } from '../app/shared/live-region/live-region.config';
|
import { LiveRegionConfig } from '../app/shared/live-region/live-region.config';
|
||||||
|
import { SearchConfig } from './search-page-config.interface';
|
||||||
|
|
||||||
interface AppConfig extends Config {
|
interface AppConfig extends Config {
|
||||||
ui: UIServerConfig;
|
ui: UIServerConfig;
|
||||||
@@ -50,6 +51,7 @@ interface AppConfig extends Config {
|
|||||||
vocabularies: FilterVocabularyConfig[];
|
vocabularies: FilterVocabularyConfig[];
|
||||||
comcolSelectionSort: DiscoverySortConfig;
|
comcolSelectionSort: DiscoverySortConfig;
|
||||||
liveRegion: LiveRegionConfig;
|
liveRegion: LiveRegionConfig;
|
||||||
|
search: SearchConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -23,6 +23,7 @@ import { MarkdownConfig } from './markdown-config.interface';
|
|||||||
import { FilterVocabularyConfig } from './filter-vocabulary-config';
|
import { FilterVocabularyConfig } from './filter-vocabulary-config';
|
||||||
import { DiscoverySortConfig } from './discovery-sort.config';
|
import { DiscoverySortConfig } from './discovery-sort.config';
|
||||||
import { LiveRegionConfig } from '../app/shared/live-region/live-region.config';
|
import { LiveRegionConfig } from '../app/shared/live-region/live-region.config';
|
||||||
|
import { SearchConfig } from './search-page-config.interface';
|
||||||
|
|
||||||
export class DefaultAppConfig implements AppConfig {
|
export class DefaultAppConfig implements AppConfig {
|
||||||
production = false;
|
production = false;
|
||||||
@@ -442,4 +443,8 @@ export class DefaultAppConfig implements AppConfig {
|
|||||||
messageTimeOutDurationMs: 30000,
|
messageTimeOutDurationMs: 30000,
|
||||||
isVisible: false,
|
isVisible: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
search: SearchConfig = {
|
||||||
|
filterPlaceholdersCount: 5
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
12
src/config/search-page-config.interface.ts
Normal file
12
src/config/search-page-config.interface.ts
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import { Config } from './config.interface';
|
||||||
|
|
||||||
|
export interface SearchConfig extends Config {
|
||||||
|
/**
|
||||||
|
* Number used to render n UI elements called loading skeletons that act as placeholders.
|
||||||
|
* These elements indicate that some content will be loaded in their stead.
|
||||||
|
* Since we don't know how many filters will be loaded before we receive a response from the server we use this parameter for the skeletons count.
|
||||||
|
* For instance if we set 5 then 5 loading skeletons will be visualized before the actual filters are retrieved.
|
||||||
|
*/
|
||||||
|
filterPlaceholdersCount?: number;
|
||||||
|
|
||||||
|
}
|
@@ -18,4 +18,13 @@ export interface UniversalConfig extends Config {
|
|||||||
* Paths to enable SSR for. Defaults to the home page and paths in the sitemap.
|
* Paths to enable SSR for. Defaults to the home page and paths in the sitemap.
|
||||||
*/
|
*/
|
||||||
paths: Array<string>;
|
paths: Array<string>;
|
||||||
|
/**
|
||||||
|
* Whether to enable rendering of search component on SSR
|
||||||
|
*/
|
||||||
|
enableSearchComponent: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether to enable rendering of browse component on SSR
|
||||||
|
*/
|
||||||
|
enableBrowseComponent: boolean;
|
||||||
}
|
}
|
||||||
|
@@ -10,5 +10,7 @@ export const environment: Partial<BuildConfig> = {
|
|||||||
time: false,
|
time: false,
|
||||||
inlineCriticalCss: false,
|
inlineCriticalCss: false,
|
||||||
paths: [ '/home', '/items/', '/entities/', '/collections/', '/communities/', '/bitstream/', '/bitstreams/', '/handle/' ],
|
paths: [ '/home', '/items/', '/entities/', '/collections/', '/communities/', '/bitstream/', '/bitstreams/', '/handle/' ],
|
||||||
}
|
enableSearchComponent: false,
|
||||||
|
enableBrowseComponent: false,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
@@ -13,6 +13,8 @@ export const environment: BuildConfig = {
|
|||||||
time: false,
|
time: false,
|
||||||
inlineCriticalCss: false,
|
inlineCriticalCss: false,
|
||||||
paths: [ '/home', '/items/', '/entities/', '/collections/', '/communities/', '/bitstream/', '/bitstreams/', '/handle/' ],
|
paths: [ '/home', '/items/', '/entities/', '/collections/', '/communities/', '/bitstream/', '/bitstreams/', '/handle/' ],
|
||||||
|
enableSearchComponent: false,
|
||||||
|
enableBrowseComponent: false,
|
||||||
},
|
},
|
||||||
|
|
||||||
// Angular Universal server settings.
|
// Angular Universal server settings.
|
||||||
@@ -321,4 +323,8 @@ export const environment: BuildConfig = {
|
|||||||
messageTimeOutDurationMs: 30000,
|
messageTimeOutDurationMs: 30000,
|
||||||
isVisible: false,
|
isVisible: false,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
search: {
|
||||||
|
filterPlaceholdersCount: 5
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
@@ -15,7 +15,9 @@ export const environment: Partial<BuildConfig> = {
|
|||||||
time: false,
|
time: false,
|
||||||
inlineCriticalCss: false,
|
inlineCriticalCss: false,
|
||||||
paths: [ '/home', '/items/', '/entities/', '/collections/', '/communities/', '/bitstream/', '/bitstreams/', '/handle/' ],
|
paths: [ '/home', '/items/', '/entities/', '/collections/', '/communities/', '/bitstream/', '/bitstreams/', '/handle/' ],
|
||||||
}
|
enableSearchComponent: false,
|
||||||
|
enableBrowseComponent: false,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@@ -138,4 +138,16 @@
|
|||||||
--green1: #1FB300; // This variable represents the success color for the Klaro cookie banner
|
--green1: #1FB300; // This variable represents the success color for the Klaro cookie banner
|
||||||
--button-text-color-cookie: #333; // This variable represents the text color for buttons in the Klaro cookie banner
|
--button-text-color-cookie: #333; // This variable represents the text color for buttons in the Klaro cookie banner
|
||||||
--very-dark-cyan: #215E50; // This variable represents the background color of the save cookies button
|
--very-dark-cyan: #215E50; // This variable represents the background color of the save cookies button
|
||||||
|
|
||||||
|
--ds-search-skeleton-text-height: 20px;
|
||||||
|
--ds-search-skeleton-badge-height: 18px;
|
||||||
|
--ds-search-skeleton-thumbnail-margin: 1em;
|
||||||
|
--ds-search-skeleton-text-line-count: 2;
|
||||||
|
--ds-search-skeleton-badge-width: 75px;
|
||||||
|
--ds-search-skeleton-filter-badge-width: 200px;
|
||||||
|
--ds-search-skeleton-info-width: 200px;
|
||||||
|
--ds-search-skeleton-card-height: 435px;
|
||||||
|
|
||||||
|
--ds-filters-skeleton-height: 40px;
|
||||||
|
--ds-filters-skeleton-spacing: 12px;
|
||||||
}
|
}
|
||||||
|
@@ -159,6 +159,8 @@ import { RequestCopyModule } from 'src/app/request-copy/request-copy.module';
|
|||||||
import {UserMenuComponent} from './app/shared/auth-nav-menu/user-menu/user-menu.component';
|
import {UserMenuComponent} from './app/shared/auth-nav-menu/user-menu/user-menu.component';
|
||||||
import { BrowseByComponent } from './app/shared/browse-by/browse-by.component';
|
import { BrowseByComponent } from './app/shared/browse-by/browse-by.component';
|
||||||
import { RegisterEmailFormComponent } from './app/register-email-form/register-email-form.component';
|
import { RegisterEmailFormComponent } from './app/register-email-form/register-email-form.component';
|
||||||
|
import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader';
|
||||||
|
|
||||||
|
|
||||||
const DECLARATIONS = [
|
const DECLARATIONS = [
|
||||||
FileSectionComponent,
|
FileSectionComponent,
|
||||||
@@ -305,6 +307,7 @@ const DECLARATIONS = [
|
|||||||
NgxGalleryModule,
|
NgxGalleryModule,
|
||||||
FormModule,
|
FormModule,
|
||||||
RequestCopyModule,
|
RequestCopyModule,
|
||||||
|
NgxSkeletonLoaderModule
|
||||||
],
|
],
|
||||||
declarations: DECLARATIONS,
|
declarations: DECLARATIONS,
|
||||||
exports: [
|
exports: [
|
||||||
|
@@ -18,6 +18,7 @@
|
|||||||
|
|
||||||
/* set the next two properties as `--ds-header-navbar-border-bottom-*`
|
/* set the next two properties as `--ds-header-navbar-border-bottom-*`
|
||||||
in order to keep the bottom border of the header when navbar is expanded */
|
in order to keep the bottom border of the header when navbar is expanded */
|
||||||
|
|
||||||
--ds-expandable-navbar-border-top-color: #{$white};
|
--ds-expandable-navbar-border-top-color: #{$white};
|
||||||
--ds-expandable-navbar-border-top-height: 0;
|
--ds-expandable-navbar-border-top-height: 0;
|
||||||
--ds-expandable-navbar-padding-top: 0;
|
--ds-expandable-navbar-padding-top: 0;
|
||||||
|
15
yarn.lock
15
yarn.lock
@@ -8415,6 +8415,14 @@ ngx-pagination@6.0.3:
|
|||||||
dependencies:
|
dependencies:
|
||||||
tslib "^2.3.0"
|
tslib "^2.3.0"
|
||||||
|
|
||||||
|
ngx-skeleton-loader@^7.0.0:
|
||||||
|
version "7.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/ngx-skeleton-loader/-/ngx-skeleton-loader-7.0.0.tgz#3b1325025a7208a20f3a0fdba6e578532a09cfcd"
|
||||||
|
integrity sha512-myc6GNcNhyksZrimIFkCxeihi0kQ8JhQVZiGbtiIv4gYrnnRk5nXbs3kYitK8E8OstHG+jlsmRofqGBxuIsYTA==
|
||||||
|
dependencies:
|
||||||
|
perf-marks "^1.13.4"
|
||||||
|
tslib "^2.0.0"
|
||||||
|
|
||||||
ngx-sortablejs@^11.1.0:
|
ngx-sortablejs@^11.1.0:
|
||||||
version "11.1.0"
|
version "11.1.0"
|
||||||
resolved "https://registry.npmjs.org/ngx-sortablejs/-/ngx-sortablejs-11.1.0.tgz"
|
resolved "https://registry.npmjs.org/ngx-sortablejs/-/ngx-sortablejs-11.1.0.tgz"
|
||||||
@@ -9060,6 +9068,13 @@ pend@~1.2.0:
|
|||||||
resolved "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz"
|
resolved "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz"
|
||||||
integrity sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==
|
integrity sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==
|
||||||
|
|
||||||
|
perf-marks@^1.13.4:
|
||||||
|
version "1.14.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/perf-marks/-/perf-marks-1.14.2.tgz#7511c24239b9c2071717993a33ec3057f387b8c7"
|
||||||
|
integrity sha512-N0/bQcuTlETpgox/DsXS1voGjqaoamMoiyhncgeW3rSHy/qw8URVgmPRYfFDQns/+C6yFUHDbeSBGL7ixT6Y4A==
|
||||||
|
dependencies:
|
||||||
|
tslib "^2.1.0"
|
||||||
|
|
||||||
performance-now@^2.1.0:
|
performance-now@^2.1.0:
|
||||||
version "2.1.0"
|
version "2.1.0"
|
||||||
resolved "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz"
|
resolved "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz"
|
||||||
|
Reference in New Issue
Block a user