diff --git a/resources/i18n/en.json b/resources/i18n/en.json index 2db47f300c..05d7e300b0 100644 --- a/resources/i18n/en.json +++ b/resources/i18n/en.json @@ -34,6 +34,12 @@ } }, "return": "Return" + }, + "select": { + "table": { + "title": "Title" + }, + "confirm": "Confirm selected" } }, "community": { diff --git a/src/app/+collection-page/collection-item-mapper/collection-item-mapper.component.html b/src/app/+collection-page/collection-item-mapper/collection-item-mapper.component.html index a7a1416cb0..7ba2d8d68a 100644 --- a/src/app/+collection-page/collection-item-mapper/collection-item-mapper.component.html +++ b/src/app/+collection-page/collection-item-mapper/collection-item-mapper.component.html @@ -32,7 +32,7 @@
diff --git a/src/app/+item-page/edit-item-page/item-collection-mapper/item-collection-mapper.component.html b/src/app/+item-page/edit-item-page/item-collection-mapper/item-collection-mapper.component.html index 21149eafaf..990ac70c64 100644 --- a/src/app/+item-page/edit-item-page/item-collection-mapper/item-collection-mapper.component.html +++ b/src/app/+item-page/edit-item-page/item-collection-mapper/item-collection-mapper.component.html @@ -15,7 +15,7 @@
- +
@@ -26,7 +26,11 @@
- +
diff --git a/src/app/+item-page/edit-item-page/item-collection-mapper/item-collection-mapper.component.ts b/src/app/+item-page/edit-item-page/item-collection-mapper/item-collection-mapper.component.ts index 1ecbd7d1f0..6f80fcde27 100644 --- a/src/app/+item-page/edit-item-page/item-collection-mapper/item-collection-mapper.component.ts +++ b/src/app/+item-page/edit-item-page/item-collection-mapper/item-collection-mapper.component.ts @@ -87,6 +87,16 @@ export class ItemCollectionMapperComponent implements OnInit { ); } + /** + * Map the item to the selected collections and display notifications + * @param {string[]} ids The list of collection UUID's to map the item to + */ + mapCollections(ids: string[]) { + // TODO: Map item to selected collections and display notifications + console.log('mapped to collections:'); + console.log(ids); + } + /** * Clear url parameters on tab change (temporary fix until pagination is improved) * @param event diff --git a/src/app/shared/object-select/collection-select/collection-select.component.html b/src/app/shared/object-select/collection-select/collection-select.component.html new file mode 100644 index 0000000000..551d33ba3b --- /dev/null +++ b/src/app/shared/object-select/collection-select/collection-select.component.html @@ -0,0 +1,27 @@ + + +
+ + + + + + + + + + + + + +
{{'collection.select.table.title' | translate}}
{{collection.name}}
+
+
+ +
diff --git a/src/app/shared/object-select/collection-select/collection-select.component.scss b/src/app/shared/object-select/collection-select/collection-select.component.scss new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/app/shared/object-select/collection-select/collection-select.component.spec.ts b/src/app/shared/object-select/collection-select/collection-select.component.spec.ts new file mode 100644 index 0000000000..ae4a6aa0a7 --- /dev/null +++ b/src/app/shared/object-select/collection-select/collection-select.component.spec.ts @@ -0,0 +1,126 @@ +import { CollectionSelectComponent } from './item-select.component'; +import { async, ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing'; +import { RouterTestingModule } from '@angular/router/testing'; +import { Item } from '../../../core/shared/item.model'; +import { Observable } from 'rxjs/Observable'; +import { RemoteData } from '../../../core/data/remote-data'; +import { PaginatedList } from '../../../core/data/paginated-list'; +import { PageInfo } from '../../../core/shared/page-info.model'; +import { PaginationComponentOptions } from '../../pagination/pagination-component-options.model'; +import { TranslateModule } from '@ngx-translate/core'; +import { SharedModule } from '../../shared.module'; +import { ObjectSelectServiceStub } from '../../testing/object-select-service-stub'; +import { ObjectSelectService } from '../object-select.service'; +import { HostWindowService } from '../../host-window.service'; +import { HostWindowServiceStub } from '../../testing/host-window-service-stub'; +import { NO_ERRORS_SCHEMA } from '@angular/core'; +import { By } from '@angular/platform-browser'; + +describe('ItemSelectComponent', () => { + let comp: CollectionSelectComponent; + let fixture: ComponentFixture; + let itemSelectService: ObjectSelectService; + + const mockItemList = [ + Object.assign(new Item(), { + id: 'id1', + bitstreams: Observable.of({}), + metadata: [ + { + key: 'dc.title', + language: 'en_US', + value: 'This is just a title' + }, + { + key: 'dc.type', + language: null, + value: 'Article' + }] + }), + Object.assign(new Item(), { + id: 'id2', + bitstreams: Observable.of({}), + metadata: [ + { + key: 'dc.title', + language: 'en_US', + value: 'This is just another title' + }, + { + key: 'dc.type', + language: null, + value: 'Article' + }] + }) + ]; + const mockItems = Observable.of(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), mockItemList))); + const mockPaginationOptions = Object.assign(new PaginationComponentOptions(), { + id: 'search-page-configuration', + pageSize: 10, + currentPage: 1 + }); + + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [TranslateModule.forRoot(), SharedModule, RouterTestingModule.withRoutes([])], + declarations: [], + providers: [ + { provide: ObjectSelectService, useValue: new ObjectSelectServiceStub() }, + { provide: HostWindowService, useValue: new HostWindowServiceStub(0) } + ], + schemas: [NO_ERRORS_SCHEMA] + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(CollectionSelectComponent); + comp = fixture.componentInstance; + comp.itemsRD$ = mockItems; + comp.paginationOptions = mockPaginationOptions; + fixture.detectChanges(); + itemSelectService = (comp as any).itemSelectService; + }); + + it(`should show a list of ${mockItemList.length} items`, () => { + const tbody: HTMLElement = fixture.debugElement.query(By.css('table#item-select tbody')).nativeElement; + expect(tbody.children.length).toBe(mockItemList.length); + }); + + describe('checkboxes', () => { + let checkbox: HTMLInputElement; + + beforeEach(() => { + checkbox = fixture.debugElement.query(By.css('input.item-checkbox')).nativeElement; + }); + + it('should initially be unchecked',() => { + expect(checkbox.checked).toBeFalsy(); + }); + + it('should be checked when clicked', () => { + checkbox.click(); + fixture.detectChanges(); + expect(checkbox.checked).toBeTruthy(); + }); + + it('should switch the value through item-select-service', () => { + spyOn((comp as any).itemSelectService, 'switch').and.callThrough(); + checkbox.click(); + expect((comp as any).itemSelectService.switch).toHaveBeenCalled(); + }); + }); + + describe('when confirm is clicked', () => { + let confirmButton: HTMLButtonElement; + + beforeEach(() => { + confirmButton = fixture.debugElement.query(By.css('button.item-confirm')).nativeElement; + spyOn(comp.confirm, 'emit').and.callThrough(); + }); + + it('should emit the selected items',() => { + confirmButton.click(); + expect(comp.confirm.emit).toHaveBeenCalled(); + }); + }); +}); diff --git a/src/app/shared/object-select/collection-select/collection-select.component.ts b/src/app/shared/object-select/collection-select/collection-select.component.ts new file mode 100644 index 0000000000..489e9109fc --- /dev/null +++ b/src/app/shared/object-select/collection-select/collection-select.component.ts @@ -0,0 +1,29 @@ +import { Component } from '@angular/core'; +import { Collection } from '../../../core/shared/collection.model'; +import { ObjectSelectComponent } from '../object-select/object-select.component'; +import { isNotEmpty } from '../../empty.util'; +import { ObjectSelectService } from '../object-select.service'; + +@Component({ + selector: 'ds-collection-select', + styleUrls: ['./collection-select.component.scss'], + templateUrl: './collection-select.component.html' +}) + +/** + * A component used to select collections from a specific list and returning the UUIDs of the selected collections + */ +export class CollectionSelectComponent extends ObjectSelectComponent { + + constructor(protected objectSelectService: ObjectSelectService) { + super(objectSelectService); + } + + ngOnInit(): void { + super.ngOnInit(); + if (!isNotEmpty(this.confirmButton)) { + this.confirmButton = 'collection.select.confirm'; + } + } + +} diff --git a/src/app/shared/object-select/item-select/item-select.component.html b/src/app/shared/object-select/item-select/item-select.component.html index 9c08cfae87..9546623ecf 100644 --- a/src/app/shared/object-select/item-select/item-select.component.html +++ b/src/app/shared/object-select/item-select/item-select.component.html @@ -1,29 +1,31 @@ - -
- - + + +
+
+ - - - + + + - -
{{'item.select.table.collection' | translate}} {{'item.select.table.author' | translate}} {{'item.select.table.title' | translate}}
{{(item.owningCollection | async)?.payload?.name}} {{item.filterMetadata(['dc.contributor.author', 'dc.creator', 'dc.contributor.*'])[0].value}} {{item.findMetadata("dc.title")}}
-
-
- + + +
+ + + diff --git a/src/app/shared/object-select/item-select/item-select.component.ts b/src/app/shared/object-select/item-select/item-select.component.ts index 6ada45cb3b..348be4b37d 100644 --- a/src/app/shared/object-select/item-select/item-select.component.ts +++ b/src/app/shared/object-select/item-select/item-select.component.ts @@ -6,6 +6,8 @@ import { RemoteData } from '../../../core/data/remote-data'; import { PaginatedList } from '../../../core/data/paginated-list'; import { PaginationComponentOptions } from '../../pagination/pagination-component-options.model'; import { ObjectSelectService } from '../object-select.service'; +import { ObjectSelectComponent } from '../object-select/object-select.component'; +import { isNotEmpty } from '../../empty.util'; @Component({ selector: 'ds-item-select', @@ -16,74 +18,17 @@ import { ObjectSelectService } from '../object-select.service'; /** * A component used to select items from a specific list and returning the UUIDs of the selected items */ -export class ItemSelectComponent implements OnInit { +export class ItemSelectComponent extends ObjectSelectComponent { - /** - * The list of items to display - */ - @Input() - itemsRD$: Observable>>; - - /** - * The pagination options used to display the items - */ - @Input() - paginationOptions: PaginationComponentOptions; - - /** - * The message key used for the confirm button - * @type {string} - */ - @Input() - confirmButton = 'item.select.confirm'; - - /** - * EventEmitter to return the selected UUIDs when the confirm button is pressed - * @type {EventEmitter} - */ - @Output() - confirm: EventEmitter = new EventEmitter(); - - /** - * The list of selected UUIDs - */ - selectedIds$: Observable; - - constructor(private objectelectService: ObjectSelectService) { + constructor(protected objectSelectService: ObjectSelectService) { + super(objectSelectService); } ngOnInit(): void { - this.selectedIds$ = this.objectelectService.getAllSelected(); - } - - /** - * Switch the state of a checkbox - * @param {string} id - */ - switch(id: string) { - this.objectelectService.switch(id); - } - - /** - * Get the current state of a checkbox - * @param {string} id The item's UUID - * @returns {Observable} - */ - getSelected(id: string): Observable { - return this.objectelectService.getSelected(id); - } - - /** - * Called when the confirm button is pressed - * Sends the selected UUIDs to the parent component - */ - confirmSelected() { - this.selectedIds$.pipe( - take(1) - ).subscribe((ids: string[]) => { - this.confirm.emit(ids); - this.objectelectService.reset(); - }); + super.ngOnInit(); + if (!isNotEmpty(this.confirmButton)) { + this.confirmButton = 'item.select.confirm'; + } } } diff --git a/src/app/shared/object-select/object-select/object-select.component.ts b/src/app/shared/object-select/object-select/object-select.component.ts new file mode 100644 index 0000000000..fb9e28edc2 --- /dev/null +++ b/src/app/shared/object-select/object-select/object-select.component.ts @@ -0,0 +1,86 @@ +import { Component, EventEmitter, Input, OnDestroy, OnInit, Output } from '@angular/core'; +import { take } from 'rxjs/operators'; +import { Observable } from 'rxjs/Observable'; +import { RemoteData } from '../../../core/data/remote-data'; +import { PaginatedList } from '../../../core/data/paginated-list'; +import { PaginationComponentOptions } from '../../pagination/pagination-component-options.model'; +import { ObjectSelectService } from '../object-select.service'; + +/** + * An abstract component used to select DSpaceObjects from a specific list and returning the UUIDs of the selected DSpaceObjects + */ +export abstract class ObjectSelectComponent implements OnInit, OnDestroy { + + /** + * The list of DSpaceObjects to display + */ + @Input() + dsoRD$: Observable>>; + + /** + * The pagination options used to display the DSpaceObjects + */ + @Input() + paginationOptions: PaginationComponentOptions; + + /** + * The message key used for the confirm button + * @type {string} + */ + @Input() + confirmButton: string; + + /** + * EventEmitter to return the selected UUIDs when the confirm button is pressed + * @type {EventEmitter} + */ + @Output() + confirm: EventEmitter = new EventEmitter(); + + /** + * The list of selected UUIDs + */ + selectedIds$: Observable; + + constructor(protected objectSelectService: ObjectSelectService) { + } + + ngOnInit(): void { + this.selectedIds$ = this.objectSelectService.getAllSelected(); + } + + ngOnDestroy(): void { + this.objectSelectService.reset(); + } + + /** + * Switch the state of a checkbox + * @param {string} id + */ + switch(id: string) { + this.objectSelectService.switch(id); + } + + /** + * Get the current state of a checkbox + * @param {string} id The dso's UUID + * @returns {Observable} + */ + getSelected(id: string): Observable { + return this.objectSelectService.getSelected(id); + } + + /** + * Called when the confirm button is pressed + * Sends the selected UUIDs to the parent component + */ + confirmSelected() { + this.selectedIds$.pipe( + take(1) + ).subscribe((ids: string[]) => { + this.confirm.emit(ids); + this.objectSelectService.reset(); + }); + } + +} diff --git a/src/app/shared/shared.module.ts b/src/app/shared/shared.module.ts index a10855bf5e..6cb9098410 100644 --- a/src/app/shared/shared.module.ts +++ b/src/app/shared/shared.module.ts @@ -84,6 +84,7 @@ import { CapitalizePipe } from './utils/capitalize.pipe'; import { MomentModule } from 'angular2-moment'; import { ObjectKeysPipe } from './utils/object-keys-pipe'; import { ItemSelectComponent } from './object-select/item-select/item-select.component'; +import { CollectionSelectComponent } from './object-select/collection-select/collection-select.component'; const MODULES = [ // Do NOT include UniversalModule, HttpModule, or JsonpModule here @@ -158,7 +159,8 @@ const COMPONENTS = [ TruncatablePartComponent, BrowseByComponent, InputSuggestionsComponent, - ItemSelectComponent + ItemSelectComponent, + CollectionSelectComponent ]; const ENTRY_COMPONENTS = [