Skip to content
Snippets Groups Projects
Commit ed958c12 authored by Art Lowel's avatar Art Lowel
Browse files

Merge branch 'master' into improve-i18n-files

parents 6c0aaa7b 8ffd44f1
No related branches found
No related tags found
No related merge requests found
Showing
with 547 additions and 23 deletions
......@@ -37,6 +37,7 @@ addons:
sources:
- google-chrome
packages:
- dpkg
- google-chrome-stable
language: node_js
......
......@@ -255,6 +255,18 @@
"item.edit.modify.overview.language": "Language",
"item.edit.modify.overview.value": "Value",
"item.edit.move.cancel": "Cancel",
"item.edit.move.description": "Select the collection you wish to move this item to. To narrow down the list of displayed collections, you can enter a search query in the box.",
"item.edit.move.error": "An error occured when attempting to move the item",
"item.edit.move.head": "Move item: {{id}}",
"item.edit.move.inheritpolicies.checkbox": "Inherit policies",
"item.edit.move.inheritpolicies.description": "Inherit the default policies of the destination collection",
"item.edit.move.move": "Move",
"item.edit.move.processing": "Moving...",
"item.edit.move.search.placeholder": "Enter a search query to look for collections",
"item.edit.move.success": "The item has been moved succesfully",
"item.edit.move.title": "Move item",
"item.edit.private.cancel": "Cancel",
"item.edit.private.confirm": "Make it Private",
"item.edit.private.description": "Are you sure this item should be made private in the archive?",
......
......@@ -18,6 +18,7 @@ import { ItemBitstreamsComponent } from './item-bitstreams/item-bitstreams.compo
import { ItemRelationshipsComponent } from './item-relationships/item-relationships.component';
import { EditRelationshipComponent } from './item-relationships/edit-relationship/edit-relationship.component';
import { EditRelationshipListComponent } from './item-relationships/edit-relationship-list/edit-relationship-list.component';
import { ItemMoveComponent } from './item-move/item-move.component';
/**
* Module that contains all components related to the Edit Item page administrator functionality
......@@ -44,7 +45,8 @@ import { EditRelationshipListComponent } from './item-relationships/edit-relatio
ItemBitstreamsComponent,
EditInPlaceFieldComponent,
EditRelationshipComponent,
EditRelationshipListComponent
EditRelationshipListComponent,
ItemMoveComponent,
]
})
export class EditItemPageModule {
......
......@@ -10,6 +10,7 @@ import { ItemDeleteComponent } from './item-delete/item-delete.component';
import { ItemStatusComponent } from './item-status/item-status.component';
import { ItemMetadataComponent } from './item-metadata/item-metadata.component';
import { ItemBitstreamsComponent } from './item-bitstreams/item-bitstreams.component';
import { ItemMoveComponent } from './item-move/item-move.component';
import { ItemRelationshipsComponent } from './item-relationships/item-relationships.component';
const ITEM_EDIT_WITHDRAW_PATH = 'withdraw';
......@@ -17,6 +18,7 @@ const ITEM_EDIT_REINSTATE_PATH = 'reinstate';
const ITEM_EDIT_PRIVATE_PATH = 'private';
const ITEM_EDIT_PUBLIC_PATH = 'public';
const ITEM_EDIT_DELETE_PATH = 'delete';
const ITEM_EDIT_MOVE_PATH = 'move';
/**
* Routing module that handles the routing for the Edit Item page administrator functionality
......@@ -104,6 +106,14 @@ const ITEM_EDIT_DELETE_PATH = 'delete';
resolve: {
item: ItemPageResolver
}
},
{
path: ITEM_EDIT_MOVE_PATH,
component: ItemMoveComponent,
data: { title: 'item.edit.move.title' },
resolve: {
item: ItemPageResolver
}
}])
],
providers: [
......
......@@ -4,7 +4,7 @@
<span>{{metadata?.key?.split('.').join('.&#8203;')}}</span>
</div>
<div *ngIf="(editable | async)" class="field-container">
<ds-input-suggestions [suggestions]="(metadataFieldSuggestions | async)"
<ds-filter-input-suggestions [suggestions]="(metadataFieldSuggestions | async)"
[(ngModel)]="metadata.key"
(submitSuggestion)="update(suggestionControl)"
(clickSuggestion)="update(suggestionControl)"
......@@ -16,7 +16,7 @@
[valid]="(valid | async) !== false"
dsAutoFocus autoFocusSelector=".suggestion_input"
[ngModelOptions]="{standalone: true}"
></ds-input-suggestions>
></ds-filter-input-suggestions>
</div>
<small class="text-danger"
*ngIf="(valid | async) === false">{{"item.edit.metadata.metadatafield.invalid" | translate}}</small>
......
......@@ -10,7 +10,6 @@ import { By } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { SharedModule } from '../../../../shared/shared.module';
import { getTestScheduler } from 'jasmine-marbles';
import { InputSuggestion } from '../../../../shared/input-suggestions/input-suggestions.model';
import { TestScheduler } from 'rxjs/testing';
import { FieldChangeType } from '../../../../core/data/object-updates/object-updates.actions';
import { TranslateModule } from '@ngx-translate/core';
......@@ -18,6 +17,7 @@ import { MetadatumViewModel } from '../../../../core/shared/metadata.models';
import { MetadataSchema } from '../../../../core/metadata/metadata-schema.model';
import { MetadataField } from '../../../../core/metadata/metadata-field.model';
import { createSuccessfulRemoteDataObject$ } from '../../../../shared/testing/utils';
import { InputSuggestion } from '../../../../shared/input-suggestions/input-suggestions.model';
let comp: EditInPlaceFieldComponent;
let fixture: ComponentFixture<EditInPlaceFieldComponent>;
......
......@@ -4,13 +4,13 @@ import { RegistryService } from '../../../../core/registry/registry.service';
import { cloneDeep } from 'lodash';
import { BehaviorSubject, Observable, of as observableOf } from 'rxjs';
import { map, take } from 'rxjs/operators';
import { InputSuggestion } from '../../../../shared/input-suggestions/input-suggestions.model';
import { FieldChangeType } from '../../../../core/data/object-updates/object-updates.actions';
import { FieldUpdate } from '../../../../core/data/object-updates/object-updates.reducer';
import { ObjectUpdatesService } from '../../../../core/data/object-updates/object-updates.service';
import { NgModel } from '@angular/forms';
import { MetadatumViewModel } from '../../../../core/shared/metadata.models';
import { MetadataField } from '../../../../core/metadata/metadata-field.model';
import { InputSuggestion } from '../../../../shared/input-suggestions/input-suggestions.model';
@Component({
// tslint:disable-next-line:component-selector
......
<div class="container">
<div class="row">
<div class="col-12">
<h2>{{'item.edit.move.head' | translate: {id: (itemRD$ | async)?.payload?.handle} }}</h2>
<p>{{'item.edit.move.description' | translate}}</p>
<div class="row">
<div class="col-12">
<ds-dso-input-suggestions #f id="search-form"
[suggestions]="(collectionSearchResults | async)"
[placeholder]="'item.edit.move.search.placeholder'| translate"
[action]="getCurrentUrl()"
[name]="'item-move'"
[(ngModel)]="selectedCollectionName"
(clickSuggestion)="onClick($event)"
(typeSuggestion)="resetCollection($event)"
(findSuggestions)="findSuggestions($event)"
(click)="f.open()"
ngDefaultControl>
</ds-dso-input-suggestions>
</div>
</div>
<div class="row">
<div class="col-12">
<p>
<input type="checkbox" name="tc" [(ngModel)]="inheritPolicies" id="inheritPoliciesCheckbox">
<label for="inheritPoliciesCheckbox">{{'item.edit.move.inheritpolicies.checkbox' |
translate}}</label>
</p>
<p>
{{'item.edit.move.inheritpolicies.description' | translate}}
</p>
</div>
</div>
<button (click)="moveCollection()" class="btn btn-primary" [disabled]=!canSubmit>
<span *ngIf="!processing"> {{'item.edit.move.move' | translate}}</span>
<span *ngIf="processing"><i class='fas fa-circle-notch fa-spin'></i>
{{'item.edit.move.processing' | translate}}
</span>
</button>
<button [routerLink]="['/items/', (itemRD$ | async)?.payload?.id, 'edit']"
class="btn btn-outline-secondary">
{{'item.edit.move.cancel' | translate}}
</button>
</div>
</div>
</div>
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { Item } from '../../../core/shared/item.model';
import { RouterStub } from '../../../shared/testing/router-stub';
import { CommonModule } from '@angular/common';
import { RouterTestingModule } from '@angular/router/testing';
import { TranslateModule } from '@ngx-translate/core';
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
import { ActivatedRoute, Router } from '@angular/router';
import { ItemMoveComponent } from './item-move.component';
import { NotificationsServiceStub } from '../../../shared/testing/notifications-service-stub';
import { NotificationsService } from '../../../shared/notifications/notifications.service';
import { SearchService } from '../../../+search-page/search-service/search.service';
import { of as observableOf } from 'rxjs';
import { FormsModule } from '@angular/forms';
import { ItemDataService } from '../../../core/data/item-data.service';
import { RemoteData } from '../../../core/data/remote-data';
import { PaginatedList } from '../../../core/data/paginated-list';
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { RestResponse } from '../../../core/cache/response.models';
import { Collection } from '../../../core/shared/collection.model';
describe('ItemMoveComponent', () => {
let comp: ItemMoveComponent;
let fixture: ComponentFixture<ItemMoveComponent>;
const mockItem = Object.assign(new Item(), {
id: 'fake-id',
handle: 'fake/handle',
lastModified: '2018'
});
const itemPageUrl = `fake-url/${mockItem.id}`;
const routerStub = Object.assign(new RouterStub(), {
url: `${itemPageUrl}/edit`
});
const mockItemDataService = jasmine.createSpyObj({
moveToCollection: observableOf(new RestResponse(true, 200, 'Success'))
});
const mockItemDataServiceFail = jasmine.createSpyObj({
moveToCollection: observableOf(new RestResponse(false, 500, 'Internal server error'))
});
const routeStub = {
data: observableOf({
item: new RemoteData(false, false, true, null, {
id: 'item1'
})
})
};
const collection1 = Object.assign(new Collection(),{
uuid: 'collection-uuid-1',
name: 'Test collection 1',
self: 'self-link-1',
});
const collection2 = Object.assign(new Collection(),{
uuid: 'collection-uuid-2',
name: 'Test collection 2',
self: 'self-link-2',
});
const mockSearchService = {
search: () => {
return observableOf(new RemoteData(false, false, true, null,
new PaginatedList(null, [
{
indexableObject: collection1,
hitHighlights: {}
}, {
indexableObject: collection2,
hitHighlights: {}
}
])));
}
};
const notificationsServiceStub = new NotificationsServiceStub();
describe('ItemMoveComponent success', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [CommonModule, FormsModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule.forRoot()],
declarations: [ItemMoveComponent],
providers: [
{provide: ActivatedRoute, useValue: routeStub},
{provide: Router, useValue: routerStub},
{provide: ItemDataService, useValue: mockItemDataService},
{provide: NotificationsService, useValue: notificationsServiceStub},
{provide: SearchService, useValue: mockSearchService},
], schemas: [
CUSTOM_ELEMENTS_SCHEMA
]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(ItemMoveComponent);
comp = fixture.componentInstance;
fixture.detectChanges();
});
it('should load suggestions', () => {
const expected = [
collection1,
collection2
];
comp.collectionSearchResults.subscribe((value) => {
expect(value).toEqual(expected);
}
);
});
it('should get current url ', () => {
expect(comp.getCurrentUrl()).toEqual('fake-url/fake-id/edit');
});
it('should on click select the correct collection name and id', () => {
const data = collection1;
comp.onClick(data);
expect(comp.selectedCollectionName).toEqual('Test collection 1');
expect(comp.selectedCollection).toEqual(collection1);
});
describe('moveCollection', () => {
it('should call itemDataService.moveToCollection', () => {
comp.itemId = 'item-id';
comp.selectedCollectionName = 'selected-collection-id';
comp.selectedCollection = collection1;
comp.moveCollection();
expect(mockItemDataService.moveToCollection).toHaveBeenCalledWith('item-id', collection1);
});
it('should call notificationsService success message on success', () => {
comp.moveCollection();
expect(notificationsServiceStub.success).toHaveBeenCalled();
});
});
});
describe('ItemMoveComponent fail', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [CommonModule, FormsModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule.forRoot()],
declarations: [ItemMoveComponent],
providers: [
{provide: ActivatedRoute, useValue: routeStub},
{provide: Router, useValue: routerStub},
{provide: ItemDataService, useValue: mockItemDataServiceFail},
{provide: NotificationsService, useValue: notificationsServiceStub},
{provide: SearchService, useValue: mockSearchService},
], schemas: [
CUSTOM_ELEMENTS_SCHEMA
]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(ItemMoveComponent);
comp = fixture.componentInstance;
fixture.detectChanges();
});
it('should call notificationsService error message on fail', () => {
comp.moveCollection();
expect(notificationsServiceStub.error).toHaveBeenCalled();
});
});
});
import { Component, OnInit } from '@angular/core';
import { SearchService } from '../../../+search-page/search-service/search.service';
import { first, map } from 'rxjs/operators';
import { DSpaceObjectType } from '../../../core/shared/dspace-object-type.model';
import { SearchOptions } from '../../../+search-page/search-options.model';
import { RemoteData } from '../../../core/data/remote-data';
import { DSpaceObject } from '../../../core/shared/dspace-object.model';
import { PaginatedList } from '../../../core/data/paginated-list';
import { SearchResult } from '../../../+search-page/search-result.model';
import { Item } from '../../../core/shared/item.model';
import { ActivatedRoute, Router } from '@angular/router';
import { NotificationsService } from '../../../shared/notifications/notifications.service';
import { TranslateService } from '@ngx-translate/core';
import { getSucceededRemoteData } from '../../../core/shared/operators';
import { ItemDataService } from '../../../core/data/item-data.service';
import { getItemEditPath } from '../../item-page-routing.module';
import { Observable, of as observableOf } from 'rxjs';
import { RestResponse } from '../../../core/cache/response.models';
import { Collection } from '../../../core/shared/collection.model';
import { tap } from 'rxjs/internal/operators/tap';
import { PaginationComponentOptions } from '../../../shared/pagination/pagination-component-options.model';
import { PaginatedSearchOptions } from '../../../+search-page/paginated-search-options.model';
@Component({
selector: 'ds-item-move',
templateUrl: './item-move.component.html'
})
/**
* Component that handles the moving of an item to a different collection
*/
export class ItemMoveComponent implements OnInit {
/**
* TODO: There is currently no backend support to change the owningCollection and inherit policies,
* TODO: when this is added, the inherit policies option should be used.
*/
selectorType = DSpaceObjectType.COLLECTION;
inheritPolicies = false;
itemRD$: Observable<RemoteData<Item>>;
collectionSearchResults: Observable<any[]> = observableOf([]);
selectedCollectionName: string;
selectedCollection: Collection;
canSubmit = false;
itemId: string;
processing = false;
pagination = new PaginationComponentOptions();
constructor(private route: ActivatedRoute,
private router: Router,
private notificationsService: NotificationsService,
private itemDataService: ItemDataService,
private searchService: SearchService,
private translateService: TranslateService) {
}
ngOnInit(): void {
this.itemRD$ = this.route.data.pipe(map((data) => data.item), getSucceededRemoteData()) as Observable<RemoteData<Item>>;
this.itemRD$.subscribe((rd) => {
this.itemId = rd.payload.id;
}
);
this.pagination.pageSize = 5;
this.loadSuggestions('');
}
/**
* Find suggestions based on entered query
* @param query - Search query
*/
findSuggestions(query): void {
this.loadSuggestions(query);
}
/**
* Load all available collections to move the item to.
* TODO: When the API support it, only fetch collections where user has ADD rights to.
*/
loadSuggestions(query): void {
this.collectionSearchResults = this.searchService.search(new PaginatedSearchOptions({
pagination: this.pagination,
dsoType: DSpaceObjectType.COLLECTION,
query: query
})).pipe(
first(),
map((rd: RemoteData<PaginatedList<SearchResult<DSpaceObject>>>) => {
return rd.payload.page.map((searchResult) => {
return searchResult.indexableObject
})
}) ,
);
}
/**
* Set the collection name and id based on the selected value
* @param data - obtained from the ds-input-suggestions component
*/
onClick(data: any): void {
this.selectedCollection = data;
this.selectedCollectionName = data.name;
this.canSubmit = true;
}
/**
* @returns {string} the current URL
*/
getCurrentUrl() {
return this.router.url;
}
/**
* Moves the item to a new collection based on the selected collection
*/
moveCollection() {
this.processing = true;
this.itemDataService.moveToCollection(this.itemId, this.selectedCollection).pipe(first()).subscribe(
(response: RestResponse) => {
this.router.navigate([getItemEditPath(this.itemId)]);
if (response.isSuccessful) {
this.notificationsService.success(this.translateService.get('item.edit.move.success'));
} else {
this.notificationsService.error(this.translateService.get('item.edit.move.error'));
}
this.processing = false;
}
);
}
/**
* Resets the can submit when the user changes the content of the input field
* @param data
*/
resetCollection(data: any) {
this.canSubmit = false;
}
}
......@@ -4,7 +4,7 @@
</span>
</div>
<div *ngIf="!operation.disabled" class="col-9 float-left action-button">
<a class="btn btn-outline-secondary" href="{{operation.operationUrl}}">
<a class="btn btn-outline-secondary" [routerLink]="operation.operationUrl">
{{'item.edit.tabs.status.buttons.' + operation.operationKey + '.button' | translate}}
</a>
</div>
......
import {ItemOperation} from './itemOperation.model';
import {async, TestBed} from '@angular/core/testing';
import {ItemOperationComponent} from './item-operation.component';
import {TranslateModule} from '@ngx-translate/core';
import {By} from '@angular/platform-browser';
import { ItemOperation } from './itemOperation.model';
import { async, TestBed } from '@angular/core/testing';
import { ItemOperationComponent } from './item-operation.component';
import { TranslateModule } from '@ngx-translate/core';
import { By } from '@angular/platform-browser';
import { RouterTestingModule } from '@angular/router/testing';
describe('ItemOperationComponent', () => {
let itemOperation: ItemOperation;
......@@ -12,7 +13,7 @@ describe('ItemOperationComponent', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [TranslateModule.forRoot()],
imports: [TranslateModule.forRoot(), RouterTestingModule.withRoutes([])],
declarations: [ItemOperationComponent]
}).compileComponents();
}));
......
......@@ -79,6 +79,7 @@ export class ItemStatusComponent implements OnInit {
this.operations.push(new ItemOperation('public', this.getCurrentUrl(item) + '/public'));
}
this.operations.push(new ItemOperation('delete', this.getCurrentUrl(item) + '/delete'));
this.operations.push(new ItemOperation('move', this.getCurrentUrl(item) + '/move'));
});
}
......
......@@ -15,7 +15,7 @@
| translate}}</a>
</div>
</div>
<ds-input-suggestions [suggestions]="(filterSearchResults | async)"
<ds-filter-input-suggestions [suggestions]="(filterSearchResults | async)"
[placeholder]="'search.filters.filter.' + filterConfig.name + '.placeholder'| translate"
[action]="currentUrl"
[name]="filterConfig.paramName"
......@@ -23,5 +23,5 @@
(submitSuggestion)="onSubmit($event)"
(clickSuggestion)="onSubmit($event)"
(findSuggestions)="findSuggestions($event)"
ngDefaultControl></ds-input-suggestions>
ngDefaultControl></ds-filter-input-suggestions>
</div>
......@@ -21,9 +21,9 @@ import { SearchService } from '../../../search-service/search.service';
import { FILTER_CONFIG, IN_PLACE_SEARCH, SearchFilterService } from '../search-filter.service';
import { SearchConfigurationService } from '../../../search-service/search-configuration.service';
import { getSucceededRemoteData } from '../../../../core/shared/operators';
import { InputSuggestion } from '../../../../shared/input-suggestions/input-suggestions.model';
import { SearchOptions } from '../../../search-options.model';
import { SEARCH_CONFIG_SERVICE } from '../../../../+my-dspace-page/my-dspace-page.component';
import { InputSuggestion } from '../../../../shared/input-suggestions/input-suggestions.model';
@Component({
selector: 'ds-search-facet-filter',
......
......@@ -15,7 +15,7 @@
| translate}}</a>
</div>
</div>
<ds-input-suggestions [suggestions]="(filterSearchResults | async)"
<ds-filter-input-suggestions [suggestions]="(filterSearchResults | async)"
[placeholder]="'search.filters.filter.' + filterConfig.name + '.placeholder'| translate"
[action]="currentUrl"
[name]="filterConfig.paramName"
......@@ -24,5 +24,5 @@
(clickSuggestion)="onClick($event)"
(findSuggestions)="findSuggestions($event)"
ngDefaultControl
></ds-input-suggestions>
></ds-filter-input-suggestions>
</div>
......@@ -15,7 +15,7 @@
| translate}}</a>
</div>
</div>
<ds-input-suggestions [suggestions]="(filterSearchResults | async)"
<ds-filter-input-suggestions [suggestions]="(filterSearchResults | async)"
[placeholder]="'search.filters.filter.' + filterConfig.name + '.placeholder'| translate"
[action]="currentUrl"
[name]="filterConfig.paramName"
......@@ -23,5 +23,5 @@
(submitSuggestion)="onSubmit($event)"
(clickSuggestion)="onClick($event)"
(findSuggestions)="findSuggestions($event)"
ngDefaultControl></ds-input-suggestions>
ngDefaultControl></ds-filter-input-suggestions>
</div>
import { distinctUntilChanged, filter, map } from 'rxjs/operators';
import { distinctUntilChanged, filter, find, map } from 'rxjs/operators';
import { Injectable } from '@angular/core';
import { Store } from '@ngrx/store';
import { Observable } from 'rxjs';
import { isNotEmpty } from '../../shared/empty.util';
import { hasValue, isNotEmpty } from '../../shared/empty.util';
import { BrowseService } from '../browse/browse.service';
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
import { CoreState } from '../core.reducers';
......@@ -12,14 +12,17 @@ import { URLCombiner } from '../url-combiner/url-combiner';
import { DataService } from './data.service';
import { RequestService } from './request.service';
import { HALEndpointService } from '../shared/hal-endpoint.service';
import { FindAllOptions, PatchRequest, RestRequest } from './request.models';
import { FindAllOptions, PatchRequest, PutRequest, RestRequest } from './request.models';
import { ObjectCacheService } from '../cache/object-cache.service';
import { NotificationsService } from '../../shared/notifications/notifications.service';
import { DSOChangeAnalyzer } from './dso-change-analyzer.service';
import { HttpClient } from '@angular/common/http';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { NormalizedObjectBuildService } from '../cache/builders/normalized-object-build.service';
import { configureRequest, getRequestFromRequestHref } from '../shared/operators';
import { RequestEntry } from './request.reducer';
import { RestResponse } from '../cache/response.models';
import { HttpOptions } from '../dspace-rest-v2/dspace-rest-v2.service';
import { Collection } from '../shared/collection.model';
@Injectable()
export class ItemDataService extends DataService<Item> {
......@@ -118,4 +121,43 @@ export class ItemDataService extends DataService<Item> {
map((requestEntry: RequestEntry) => requestEntry.response)
);
}
/**
* Get the endpoint to move the item
* @param itemId
*/
public getMoveItemEndpoint(itemId: string): Observable<string> {
return this.halService.getEndpoint(this.linkPath).pipe(
map((endpoint: string) => this.getIDHref(endpoint, itemId)),
map((endpoint: string) => `${endpoint}/owningCollection`)
);
}
/**
* Move the item to a different owning collection
* @param itemId
* @param collection
*/
public moveToCollection(itemId: string, collection: Collection): Observable<RestResponse> {
const options: HttpOptions = Object.create({});
let headers = new HttpHeaders();
headers = headers.append('Content-Type', 'text/uri-list');
options.headers = headers;
const requestId = this.requestService.generateRequestId();
const hrefObs = this.getMoveItemEndpoint(itemId);
hrefObs.pipe(
find((href: string) => hasValue(href)),
map((href: string) => {
const request = new PutRequest(requestId, href, collection.self, options);
this.requestService.configure(request);
})
).subscribe();
return this.requestService.getByUUID(requestId).pipe(
find((request: RequestEntry) => request.completed),
map((request: RequestEntry) => request.response)
);
}
}
<form #form="ngForm" (ngSubmit)="onSubmit(currentObject)"
[action]="action" (keydown)="onKeydown($event)"
(keydown.arrowdown)="shiftFocusDown($event)"
(keydown.arrowup)="shiftFocusUp($event)" (keydown.esc)="close()"
(dsClickOutside)="close();">
<input #inputField type="text" [(ngModel)]="value" [name]="name"
class="form-control suggestion_input mb-2"
[ngClass]="{'is-invalid': !valid}"
[dsDebounce]="debounceTime" (onDebounce)="find($event)"
[placeholder]="placeholder"
[ngModelOptions]="{standalone: true}" autocomplete="off"/>
<input type="submit" class="d-none"/>
<div class="autocomplete dropdown-menu" [ngClass]="{'show': (show | async) && isNotEmpty(suggestions)}">
<div class="dropdown-list">
<div *ngFor="let suggestionOption of suggestions">
<button class="d-block dropdown-item" (click)="onClickSuggestion(suggestionOption)" #suggestion>
<div class="click-blocker">
</div>
<ds-wrapper-list-element [object]="suggestionOption"></ds-wrapper-list-element>
</button>
</div>
</div>
</div>
</form>
import { ChangeDetectionStrategy, DebugElement, NO_ERRORS_SCHEMA } from '@angular/core';
import { TranslateModule } from '@ngx-translate/core';
import { By } from '@angular/platform-browser';
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { FormsModule } from '@angular/forms';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { RouterTestingModule } from '@angular/router/testing';
import { DsoInputSuggestionsComponent } from './dso-input-suggestions.component';
import { DSpaceObject } from '../../../core/shared/dspace-object.model';
describe('DsoInputSuggestionsComponent', () => {
let comp: DsoInputSuggestionsComponent;
let fixture: ComponentFixture<DsoInputSuggestionsComponent>;
let de: DebugElement;
let el: HTMLElement;
const dso1 = {
uuid: 'test-uuid-1',
name: 'test-name-1'
} as DSpaceObject;
const dso2 = {
uuid: 'test-uuid-2',
name: 'test-name-2'
} as DSpaceObject;
const dso3 = {
uuid: 'test-uuid-3',
name: 'test-name-3'
} as DSpaceObject;
const suggestions = [dso1, dso2, dso3];
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [TranslateModule.forRoot(), RouterTestingModule.withRoutes([]), NoopAnimationsModule, FormsModule],
declarations: [DsoInputSuggestionsComponent],
providers: [],
schemas: [NO_ERRORS_SCHEMA]
}).overrideComponent(DsoInputSuggestionsComponent, {
set: {changeDetection: ChangeDetectionStrategy.Default}
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(DsoInputSuggestionsComponent);
comp = fixture.componentInstance; // LoadingComponent test instance
comp.suggestions = suggestions;
// query for the message <label> by CSS element selector
de = fixture.debugElement;
el = de.nativeElement;
comp.show.next(true);
fixture.detectChanges();
});
describe('when an element is clicked', () => {
const clickedIndex = 0;
beforeEach(() => {
spyOn(comp, 'onClickSuggestion');
const clickedLink = de.query(By.css('.dropdown-list > div:nth-child(' + (clickedIndex + 1) + ') button'));
clickedLink.triggerEventHandler('click', {});
fixture.detectChanges();
});
it('should call onClickSuggestion() with the suggestion as a parameter', () => {
expect(comp.onClickSuggestion).toHaveBeenCalledWith(suggestions[clickedIndex]);
});
});
});
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment