mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-07 10:04:11 +00:00
improved coverage, type docs, removed startsWith option and general refactoring
This commit is contained in:
@@ -18,6 +18,9 @@ import { Item } from '../../core/shared/item.model';
|
|||||||
styleUrls: ['./browse-by-author-page.component.scss'],
|
styleUrls: ['./browse-by-author-page.component.scss'],
|
||||||
templateUrl: './browse-by-author-page.component.html'
|
templateUrl: './browse-by-author-page.component.html'
|
||||||
})
|
})
|
||||||
|
/**
|
||||||
|
* Component for browsing (items) by author (dc.contributor.author)
|
||||||
|
*/
|
||||||
export class BrowseByAuthorPageComponent implements OnInit {
|
export class BrowseByAuthorPageComponent implements OnInit {
|
||||||
|
|
||||||
authors$: Observable<RemoteData<PaginatedList<BrowseEntry>>>;
|
authors$: Observable<RemoteData<PaginatedList<BrowseEntry>>>;
|
||||||
@@ -55,7 +58,6 @@ export class BrowseByAuthorPageComponent implements OnInit {
|
|||||||
const pageSize = +params.pageSize || this.paginationConfig.pageSize;
|
const pageSize = +params.pageSize || this.paginationConfig.pageSize;
|
||||||
const sortDirection = params.sortDirection || this.sortConfig.direction;
|
const sortDirection = params.sortDirection || this.sortConfig.direction;
|
||||||
const sortField = params.sortField || this.sortConfig.field;
|
const sortField = params.sortField || this.sortConfig.field;
|
||||||
const startsWith = +params.query || params.query || '';
|
|
||||||
this.value = +params.value || params.value || '';
|
this.value = +params.value || params.value || '';
|
||||||
const pagination = Object.assign({},
|
const pagination = Object.assign({},
|
||||||
this.paginationConfig,
|
this.paginationConfig,
|
||||||
@@ -67,8 +69,7 @@ export class BrowseByAuthorPageComponent implements OnInit {
|
|||||||
);
|
);
|
||||||
const searchOptions = {
|
const searchOptions = {
|
||||||
pagination: pagination,
|
pagination: pagination,
|
||||||
sort: sort,
|
sort: sort
|
||||||
startsWith: startsWith
|
|
||||||
};
|
};
|
||||||
if (isNotEmpty(this.value)) {
|
if (isNotEmpty(this.value)) {
|
||||||
this.updatePageWithItems(searchOptions, this.value);
|
this.updatePageWithItems(searchOptions, this.value);
|
||||||
@@ -79,10 +80,10 @@ export class BrowseByAuthorPageComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Updates the current page with searchOptions
|
||||||
* @param searchOptions Options to narrow down your search:
|
* @param searchOptions Options to narrow down your search:
|
||||||
* { pagination: PaginationComponentOptions,
|
* { pagination: PaginationComponentOptions,
|
||||||
* sort: SortOptions,
|
* sort: SortOptions }
|
||||||
* startsWith: string }
|
|
||||||
*/
|
*/
|
||||||
updatePage(searchOptions) {
|
updatePage(searchOptions) {
|
||||||
this.authors$ = this.browseService.getBrowseEntriesFor('author', searchOptions);
|
this.authors$ = this.browseService.getBrowseEntriesFor('author', searchOptions);
|
||||||
@@ -90,10 +91,10 @@ export class BrowseByAuthorPageComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Updates the current page with searchOptions and display items linked to author
|
||||||
* @param searchOptions Options to narrow down your search:
|
* @param searchOptions Options to narrow down your search:
|
||||||
* { pagination: PaginationComponentOptions,
|
* { pagination: PaginationComponentOptions,
|
||||||
* sort: SortOptions,
|
* sort: SortOptions }
|
||||||
* startsWith: string }
|
|
||||||
* @param author The author's name for displaying items
|
* @param author The author's name for displaying items
|
||||||
*/
|
*/
|
||||||
updatePageWithItems(searchOptions, author: string) {
|
updatePageWithItems(searchOptions, author: string) {
|
||||||
|
@@ -17,6 +17,9 @@ import { Collection } from '../../core/shared/collection.model';
|
|||||||
styleUrls: ['./browse-by-title-page.component.scss'],
|
styleUrls: ['./browse-by-title-page.component.scss'],
|
||||||
templateUrl: './browse-by-title-page.component.html'
|
templateUrl: './browse-by-title-page.component.html'
|
||||||
})
|
})
|
||||||
|
/**
|
||||||
|
* Component for browsing items by title (dc.title)
|
||||||
|
*/
|
||||||
export class BrowseByTitlePageComponent implements OnInit {
|
export class BrowseByTitlePageComponent implements OnInit {
|
||||||
|
|
||||||
items$: Observable<RemoteData<PaginatedList<Item>>>;
|
items$: Observable<RemoteData<PaginatedList<Item>>>;
|
||||||
@@ -52,7 +55,6 @@ export class BrowseByTitlePageComponent implements OnInit {
|
|||||||
const page = +params.page || this.paginationConfig.currentPage;
|
const page = +params.page || this.paginationConfig.currentPage;
|
||||||
const pageSize = +params.pageSize || this.paginationConfig.pageSize;
|
const pageSize = +params.pageSize || this.paginationConfig.pageSize;
|
||||||
const sortDirection = +params.page || this.sortConfig.direction;
|
const sortDirection = +params.page || this.sortConfig.direction;
|
||||||
const startsWith = +params.query || params.query || '';
|
|
||||||
const pagination = Object.assign({},
|
const pagination = Object.assign({},
|
||||||
this.paginationConfig,
|
this.paginationConfig,
|
||||||
{ currentPage: page, pageSize: pageSize }
|
{ currentPage: page, pageSize: pageSize }
|
||||||
@@ -63,18 +65,22 @@ export class BrowseByTitlePageComponent implements OnInit {
|
|||||||
);
|
);
|
||||||
this.updatePage({
|
this.updatePage({
|
||||||
pagination: pagination,
|
pagination: pagination,
|
||||||
sort: sort,
|
sort: sort
|
||||||
startsWith: startsWith
|
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the current page with searchOptions
|
||||||
|
* @param searchOptions Options to narrow down your search:
|
||||||
|
* { pagination: PaginationComponentOptions,
|
||||||
|
* sort: SortOptions }
|
||||||
|
*/
|
||||||
updatePage(searchOptions) {
|
updatePage(searchOptions) {
|
||||||
this.items$ = this.itemDataService.findAll({
|
this.items$ = this.itemDataService.findAll({
|
||||||
currentPage: searchOptions.pagination.currentPage,
|
currentPage: searchOptions.pagination.currentPage,
|
||||||
elementsPerPage: searchOptions.pagination.pageSize,
|
elementsPerPage: searchOptions.pagination.pageSize,
|
||||||
sort: searchOptions.sort,
|
sort: searchOptions.sort
|
||||||
startsWith: searchOptions.startsWith
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -6,7 +6,7 @@ import { getMockResponseCacheService } from '../../shared/mocks/mock-response-ca
|
|||||||
import { HALEndpointServiceStub } from '../../shared/testing/hal-endpoint-service-stub';
|
import { HALEndpointServiceStub } from '../../shared/testing/hal-endpoint-service-stub';
|
||||||
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
||||||
import { ResponseCacheService } from '../cache/response-cache.service';
|
import { ResponseCacheService } from '../cache/response-cache.service';
|
||||||
import { BrowseEndpointRequest, BrowseEntriesRequest } from '../data/request.models';
|
import { BrowseEndpointRequest, BrowseEntriesRequest, BrowseItemsRequest } from '../data/request.models';
|
||||||
import { RequestService } from '../data/request.service';
|
import { RequestService } from '../data/request.service';
|
||||||
import { BrowseDefinition } from '../shared/browse-definition.model';
|
import { BrowseDefinition } from '../shared/browse-definition.model';
|
||||||
import { BrowseService } from './browse.service';
|
import { BrowseService } from './browse.service';
|
||||||
@@ -143,7 +143,9 @@ describe('BrowseService', () => {
|
|||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('getBrowseEntriesFor', () => {
|
describe('getBrowseEntriesFor and getBrowseItemsFor', () => {
|
||||||
|
const mockAuthorName = 'Donald Smith';
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
responseCache = initMockResponseCacheService(true);
|
responseCache = initMockResponseCacheService(true);
|
||||||
requestService = getMockRequestService();
|
requestService = getMockRequestService();
|
||||||
@@ -156,7 +158,7 @@ describe('BrowseService', () => {
|
|||||||
spyOn(rdbService, 'toRemoteDataObservable').and.callThrough();
|
spyOn(rdbService, 'toRemoteDataObservable').and.callThrough();
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('when called with a valid browse definition id', () => {
|
describe('when getBrowseEntriesFor is called with a valid browse definition id', () => {
|
||||||
it('should configure a new BrowseEntriesRequest', () => {
|
it('should configure a new BrowseEntriesRequest', () => {
|
||||||
const expected = new BrowseEntriesRequest(requestService.generateRequestId(), browseDefinitions[1]._links.entries);
|
const expected = new BrowseEntriesRequest(requestService.generateRequestId(), browseDefinitions[1]._links.entries);
|
||||||
|
|
||||||
@@ -175,7 +177,26 @@ describe('BrowseService', () => {
|
|||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('when called with an invalid browse definition id', () => {
|
describe('when getBrowseItemsFor is called with a valid browse definition id', () => {
|
||||||
|
it('should configure a new BrowseItemsRequest', () => {
|
||||||
|
const expected = new BrowseItemsRequest(requestService.generateRequestId(), browseDefinitions[1]._links.items + '?filterValue=' + mockAuthorName);
|
||||||
|
|
||||||
|
scheduler.schedule(() => service.getBrowseItemsFor(browseDefinitions[1].id, mockAuthorName).subscribe());
|
||||||
|
scheduler.flush();
|
||||||
|
|
||||||
|
expect(requestService.configure).toHaveBeenCalledWith(expected);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call RemoteDataBuildService to create the RemoteData Observable', () => {
|
||||||
|
service.getBrowseItemsFor(browseDefinitions[1].id, mockAuthorName);
|
||||||
|
|
||||||
|
expect(rdbService.toRemoteDataObservable).toHaveBeenCalled();
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when getBrowseEntriesFor is called with an invalid browse definition id', () => {
|
||||||
it('should throw an Error', () => {
|
it('should throw an Error', () => {
|
||||||
|
|
||||||
const definitionID = 'invalidID';
|
const definitionID = 'invalidID';
|
||||||
@@ -184,6 +205,16 @@ describe('BrowseService', () => {
|
|||||||
expect(service.getBrowseEntriesFor(definitionID)).toBeObservable(expected);
|
expect(service.getBrowseEntriesFor(definitionID)).toBeObservable(expected);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('when getBrowseItemsFor is called with an invalid browse definition id', () => {
|
||||||
|
it('should throw an Error', () => {
|
||||||
|
|
||||||
|
const definitionID = 'invalidID';
|
||||||
|
const expected = cold('--#-', undefined, new Error(`No metadata browse definition could be found for id '${definitionID}'`))
|
||||||
|
|
||||||
|
expect(service.getBrowseItemsFor(definitionID, mockAuthorName)).toBeObservable(expected);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('getBrowseURLFor', () => {
|
describe('getBrowseURLFor', () => {
|
||||||
|
@@ -133,6 +133,15 @@ export class BrowseService {
|
|||||||
return this.rdb.toRemoteDataObservable(requestEntry$, responseCache$, payload$);
|
return this.rdb.toRemoteDataObservable(requestEntry$, responseCache$, payload$);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all items linked to a certain metadata value
|
||||||
|
* @param {string} definitionID definition ID to define the metadata-field (e.g. author)
|
||||||
|
* @param {string} filterValue metadata value to filter by (e.g. author's name)
|
||||||
|
* @param options Options to narrow down your search:
|
||||||
|
* { pagination: PaginationComponentOptions,
|
||||||
|
* sort: SortOptions }
|
||||||
|
* @returns {Observable<RemoteData<PaginatedList<Item>>>}
|
||||||
|
*/
|
||||||
getBrowseItemsFor(definitionID: string, filterValue: string, options: {
|
getBrowseItemsFor(definitionID: string, filterValue: string, options: {
|
||||||
pagination?: PaginationComponentOptions;
|
pagination?: PaginationComponentOptions;
|
||||||
sort?: SortOptions;
|
sort?: SortOptions;
|
||||||
|
@@ -21,19 +21,17 @@ describe('ItemDataService', () => {
|
|||||||
const halEndpointService = {} as HALEndpointService;
|
const halEndpointService = {} as HALEndpointService;
|
||||||
|
|
||||||
const scopeID = '4af28e99-6a9c-4036-a199-e1b587046d39';
|
const scopeID = '4af28e99-6a9c-4036-a199-e1b587046d39';
|
||||||
const startsWith = 'a';
|
|
||||||
const options = Object.assign(new FindAllOptions(), {
|
const options = Object.assign(new FindAllOptions(), {
|
||||||
scopeID: scopeID,
|
scopeID: scopeID,
|
||||||
sort: {
|
sort: {
|
||||||
field: '',
|
field: '',
|
||||||
direction: undefined
|
direction: undefined
|
||||||
},
|
}
|
||||||
startsWith: startsWith
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const browsesEndpoint = 'https://rest.api/discover/browses';
|
const browsesEndpoint = 'https://rest.api/discover/browses';
|
||||||
const itemBrowseEndpoint = `${browsesEndpoint}/author/items`;
|
const itemBrowseEndpoint = `${browsesEndpoint}/author/items`;
|
||||||
const scopedEndpoint = `${itemBrowseEndpoint}?scope=${scopeID}&startsWith=${startsWith}`;
|
const scopedEndpoint = `${itemBrowseEndpoint}?scope=${scopeID}`;
|
||||||
const serviceEndpoint = `https://rest.api/core/items`;
|
const serviceEndpoint = `https://rest.api/core/items`;
|
||||||
const browseError = new Error('getBrowseURL failed');
|
const browseError = new Error('getBrowseURL failed');
|
||||||
|
|
||||||
|
@@ -38,7 +38,7 @@ export class ItemDataService extends DataService<NormalizedItem, Item> {
|
|||||||
}
|
}
|
||||||
return this.bs.getBrowseURLFor(field, this.linkPath)
|
return this.bs.getBrowseURLFor(field, this.linkPath)
|
||||||
.filter((href: string) => isNotEmpty(href))
|
.filter((href: string) => isNotEmpty(href))
|
||||||
.map((href: string) => new URLCombiner(href, `?scope=${options.scopeID}` + (options.startsWith ? `&startsWith=${options.startsWith}` : '')).toString())
|
.map((href: string) => new URLCombiner(href, `?scope=${options.scopeID}`).toString())
|
||||||
.distinctUntilChanged();
|
.distinctUntilChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -47,6 +47,11 @@ export const getRemoteDataPayload = () =>
|
|||||||
<T>(source: Observable<RemoteData<T>>): Observable<T> =>
|
<T>(source: Observable<RemoteData<T>>): Observable<T> =>
|
||||||
source.pipe(map((remoteData: RemoteData<T>) => remoteData.payload));
|
source.pipe(map((remoteData: RemoteData<T>) => remoteData.payload));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the browse links from a definition by ID given an array of all definitions
|
||||||
|
* @param {string} definitionID
|
||||||
|
* @returns {(source: Observable<RemoteData<BrowseDefinition[]>>) => Observable<any>}
|
||||||
|
*/
|
||||||
export const getBrowseDefinitionLinks = (definitionID: string) =>
|
export const getBrowseDefinitionLinks = (definitionID: string) =>
|
||||||
(source: Observable<RemoteData<BrowseDefinition[]>>): Observable<any> =>
|
(source: Observable<RemoteData<BrowseDefinition[]>>): Observable<any> =>
|
||||||
source.pipe(
|
source.pipe(
|
||||||
|
@@ -17,6 +17,9 @@ import { ListableObject } from '../object-collection/shared/listable-object.mode
|
|||||||
fadeInOut
|
fadeInOut
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
/**
|
||||||
|
* Component to display a browse-by page for any ListableObject
|
||||||
|
*/
|
||||||
export class BrowseByComponent {
|
export class BrowseByComponent {
|
||||||
@Input() title: string;
|
@Input() title: string;
|
||||||
@Input() objects$: Observable<RemoteData<PaginatedList<ListableObject>>>;
|
@Input() objects$: Observable<RemoteData<PaginatedList<ListableObject>>>;
|
||||||
|
@@ -1,3 +0,0 @@
|
|||||||
<a [routerLink]="['/browse/' + object.key + '/' + object.value]" class="lead">
|
|
||||||
{{object.value}}
|
|
||||||
</a>
|
|
@@ -1 +0,0 @@
|
|||||||
@import '../../../../styles/variables';
|
|
@@ -1,48 +0,0 @@
|
|||||||
import { MetadataListElementComponent } from './metadata-list-element.component';
|
|
||||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
|
||||||
import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
|
|
||||||
import { By } from '@angular/platform-browser';
|
|
||||||
import { TruncatePipe } from '../../utils/truncate.pipe';
|
|
||||||
import { Item } from '../../../core/shared/item.model';
|
|
||||||
import { Observable } from 'rxjs/Observable';
|
|
||||||
import { Metadatum } from '../../../core/shared/metadatum.model';
|
|
||||||
|
|
||||||
let metadataListElementComponent: MetadataListElementComponent;
|
|
||||||
let fixture: ComponentFixture<MetadataListElementComponent>;
|
|
||||||
|
|
||||||
const mockValue: Metadatum = Object.assign(new Metadatum(), {
|
|
||||||
key: 'dc.contributor.author',
|
|
||||||
value: 'De Langhe Kristof'
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('MetadataListElementComponent', () => {
|
|
||||||
beforeEach(async(() => {
|
|
||||||
TestBed.configureTestingModule({
|
|
||||||
declarations: [ MetadataListElementComponent , TruncatePipe],
|
|
||||||
providers: [
|
|
||||||
{ provide: 'objectElementProvider', useValue: {mockValue}}
|
|
||||||
],
|
|
||||||
|
|
||||||
schemas: [ NO_ERRORS_SCHEMA ]
|
|
||||||
}).overrideComponent(MetadataListElementComponent, {
|
|
||||||
set: { changeDetection: ChangeDetectionStrategy.Default }
|
|
||||||
}).compileComponents();
|
|
||||||
}));
|
|
||||||
|
|
||||||
beforeEach(async(() => {
|
|
||||||
fixture = TestBed.createComponent(MetadataListElementComponent);
|
|
||||||
metadataListElementComponent = fixture.componentInstance;
|
|
||||||
}));
|
|
||||||
|
|
||||||
describe('When the metadatum is loaded', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
metadataListElementComponent.object = mockValue;
|
|
||||||
fixture.detectChanges();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should show the value as a link', () => {
|
|
||||||
const metadatumLink = fixture.debugElement.query(By.css('a.lead'));
|
|
||||||
expect(metadatumLink.nativeElement.textContent.trim()).toBe(mockValue.value);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
@@ -1,15 +0,0 @@
|
|||||||
import { Component, Input, Inject } from '@angular/core';
|
|
||||||
|
|
||||||
import { AbstractListableElementComponent } from '../../object-collection/shared/object-collection-element/abstract-listable-element.component';
|
|
||||||
import { renderElementsFor } from '../../object-collection/shared/dso-element-decorator';
|
|
||||||
import { ViewMode } from '../../../+search-page/search-options.model';
|
|
||||||
import { Metadatum } from '../../../core/shared/metadatum.model';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'ds-metadata-list-element',
|
|
||||||
styleUrls: ['./metadata-list-element.component.scss'],
|
|
||||||
templateUrl: './metadata-list-element.component.html'
|
|
||||||
})
|
|
||||||
|
|
||||||
@renderElementsFor(Metadatum, ViewMode.List)
|
|
||||||
export class MetadataListElementComponent extends AbstractListableElementComponent<Metadatum> {}
|
|
@@ -72,7 +72,6 @@ import { NumberPickerComponent } from './number-picker/number-picker.component';
|
|||||||
import { DsDatePickerComponent } from './form/builder/ds-dynamic-form-ui/models/date-picker/date-picker.component';
|
import { DsDatePickerComponent } from './form/builder/ds-dynamic-form-ui/models/date-picker/date-picker.component';
|
||||||
import { DsDynamicLookupComponent } from './form/builder/ds-dynamic-form-ui/models/lookup/dynamic-lookup.component';
|
import { DsDynamicLookupComponent } from './form/builder/ds-dynamic-form-ui/models/lookup/dynamic-lookup.component';
|
||||||
import { MockAdminGuard } from './mocks/mock-admin-guard.service';
|
import { MockAdminGuard } from './mocks/mock-admin-guard.service';
|
||||||
import { MetadataListElementComponent } from './object-list/metadata-list-element/metadata-list-element.component';
|
|
||||||
import { BrowseByModule } from '../+browse-by/browse-by.module';
|
import { BrowseByModule } from '../+browse-by/browse-by.module';
|
||||||
import { BrowseByComponent } from './browse-by/browse-by.component';
|
import { BrowseByComponent } from './browse-by/browse-by.component';
|
||||||
import { BrowseEntryListElementComponent } from './object-list/browse-entry-list-element/browse-entry-list-element.component';
|
import { BrowseEntryListElementComponent } from './object-list/browse-entry-list-element/browse-entry-list-element.component';
|
||||||
@@ -149,7 +148,6 @@ const COMPONENTS = [
|
|||||||
const ENTRY_COMPONENTS = [
|
const ENTRY_COMPONENTS = [
|
||||||
// put shared entry components (components that are created dynamically) here
|
// put shared entry components (components that are created dynamically) here
|
||||||
ItemListElementComponent,
|
ItemListElementComponent,
|
||||||
MetadataListElementComponent,
|
|
||||||
CollectionListElementComponent,
|
CollectionListElementComponent,
|
||||||
CommunityListElementComponent,
|
CommunityListElementComponent,
|
||||||
SearchResultListElementComponent,
|
SearchResultListElementComponent,
|
||||||
|
Reference in New Issue
Block a user