From 3dc550c23139b02c703e02bbfcf3e9970038f90c Mon Sep 17 00:00:00 2001 From: Martin Trajanovski Date: Thu, 8 Jun 2023 14:31:54 +0200 Subject: [PATCH 1/2] fix: Use of shared-table for published data representation --- .../files-dashboard.component.html | 8 +- .../jobs-dashboard-new.component.html | 9 +- .../proposal-dashboard.component.html | 10 +- .../publisheddata-dashboard.component.html | 47 +-- .../publisheddata-dashboard.component.ts | 290 ++++++++++-------- .../shared-table/shared-table.component.html | 28 +- .../shared-table/shared-table.component.scss | 8 + .../shared-table/shared-table.component.ts | 82 ++++- .../shared-table/shared-table.module.ts | 193 ++++++------ .../shared/services/scicat-data-service.ts | 16 + src/app/shared/services/scicat.datasource.ts | 17 +- 11 files changed, 429 insertions(+), 279 deletions(-) diff --git a/src/app/files/files-dashboard/files-dashboard.component.html b/src/app/files/files-dashboard/files-dashboard.component.html index 9ef86c95b..b61a861b2 100644 --- a/src/app/files/files-dashboard/files-dashboard.component.html +++ b/src/app/files/files-dashboard/files-dashboard.component.html @@ -1,2 +1,8 @@ - + diff --git a/src/app/jobs/jobs-dashboard-new/jobs-dashboard-new.component.html b/src/app/jobs/jobs-dashboard-new/jobs-dashboard-new.component.html index 4010e9be7..4ec0663a5 100644 --- a/src/app/jobs/jobs-dashboard-new/jobs-dashboard-new.component.html +++ b/src/app/jobs/jobs-dashboard-new/jobs-dashboard-new.component.html @@ -1,2 +1,7 @@ - - + + diff --git a/src/app/proposals/proposal-dashboard/proposal-dashboard.component.html b/src/app/proposals/proposal-dashboard/proposal-dashboard.component.html index 586875ac1..4ec0663a5 100644 --- a/src/app/proposals/proposal-dashboard/proposal-dashboard.component.html +++ b/src/app/proposals/proposal-dashboard/proposal-dashboard.component.html @@ -1,3 +1,7 @@ - - - + + diff --git a/src/app/publisheddata/publisheddata-dashboard/publisheddata-dashboard.component.html b/src/app/publisheddata/publisheddata-dashboard/publisheddata-dashboard.component.html index 03a37f7ea..cd01cfec0 100644 --- a/src/app/publisheddata/publisheddata-dashboard/publisheddata-dashboard.component.html +++ b/src/app/publisheddata/publisheddata-dashboard/publisheddata-dashboard.component.html @@ -1,36 +1,11 @@ - -
-
-
-
- -
- -
- - -
-
-
-
+ + diff --git a/src/app/publisheddata/publisheddata-dashboard/publisheddata-dashboard.component.ts b/src/app/publisheddata/publisheddata-dashboard/publisheddata-dashboard.component.ts index 6ee4414ac..ff3f91bf4 100644 --- a/src/app/publisheddata/publisheddata-dashboard/publisheddata-dashboard.component.ts +++ b/src/app/publisheddata/publisheddata-dashboard/publisheddata-dashboard.component.ts @@ -1,132 +1,158 @@ -import { Component, OnInit, OnDestroy, Inject } from "@angular/core"; -import { Store } from "@ngrx/store"; -import { PublishedData } from "shared/sdk"; -import { Router } from "@angular/router"; -import { selectPublishedDataDashboardPageViewModel } from "state-management/selectors/published-data.selectors"; -import { - fetchAllPublishedDataAction, - changePageAction, - sortByColumnAction, -} from "state-management/actions/published-data.actions"; -import { - PageChangeEvent, - TableColumn, - SortChangeEvent, - CheckboxEvent, -} from "shared/modules/table/table.component"; -import { Subscription } from "rxjs"; - -import { MatCheckboxChange } from "@angular/material/checkbox"; -import { DOCUMENT } from "@angular/common"; -import { take } from "rxjs/operators"; -import { Message, MessageType } from "state-management/models"; -import { showMessageAction } from "state-management/actions/user.actions"; - -@Component({ - selector: "app-publisheddata-dashboard", - templateUrl: "./publisheddata-dashboard.component.html", - styleUrls: ["./publisheddata-dashboard.component.scss"], -}) -export class PublisheddataDashboardComponent implements OnInit, OnDestroy { - public vm$ = this.store.select(selectPublishedDataDashboardPageViewModel); - - columns: TableColumn[] = [ - { name: "doi", icon: "fingerprint", sort: true, inList: false }, - { name: "title", icon: "description", sort: true, inList: true }, - { name: "creator", icon: "face", sort: true, inList: true }, - // { name: "publicationYear", icon: "date_range", sort: true, inList: true }, - { name: "createdBy", icon: "account_circle", sort: true, inList: true }, - { - name: "createdAt", - icon: "date_range", - sort: true, - inList: true, - dateFormat: "yyyy-MM-dd HH:mm", - }, - ]; - paginate = true; - select = true; - - doiBaseUrl = "https://doi.org/"; - selectedDOIs: string[] = []; - filtersSubscription: Subscription = new Subscription(); - - constructor( - @Inject(DOCUMENT) private document: Document, - private router: Router, - private store: Store - ) {} - - onShareClick() { - const selectionBox = this.document.createElement("textarea"); - selectionBox.style.position = "fixed"; - selectionBox.style.left = "0"; - selectionBox.style.top = "0"; - selectionBox.style.opacity = "0"; - selectionBox.value = this.selectedDOIs.join("\n"); - this.document.body.appendChild(selectionBox); - selectionBox.focus(); - selectionBox.select(); - this.document.execCommand("copy"); - this.document.body.removeChild(selectionBox); - - const message = new Message( - "The selected DOI's have been copied to your clipboard", - MessageType.Success, - 5000 - ); - this.store.dispatch(showMessageAction({ message })); - } - - onPageChange(event: PageChangeEvent) { - this.store.dispatch( - changePageAction({ page: event.pageIndex, limit: event.pageSize }) - ); - } - - onSortChange(event: SortChangeEvent) { - const { active: column, direction } = event; - this.store.dispatch(sortByColumnAction({ column, direction })); - } - - onRowClick(published: PublishedData) { - const id = encodeURIComponent(published.doi); - this.router.navigateByUrl("/publishedDatasets/" + id); - } - - onSelectAll(event: MatCheckboxChange) { - if (event.checked) { - this.vm$.pipe(take(1)).subscribe((vm) => { - this.selectedDOIs = vm.publishedData.map( - ({ doi }) => this.doiBaseUrl + encodeURIComponent(doi) - ); - }); - } else { - this.selectedDOIs = []; - } - } - - onSelectOne(checkboxEvent: CheckboxEvent) { - const { event, row } = checkboxEvent; - const doiUrl = this.doiBaseUrl + encodeURIComponent(row.doi); - if (event.checked) { - this.selectedDOIs.push(doiUrl); - } else { - this.selectedDOIs.splice(this.selectedDOIs.indexOf(doiUrl), 1); - } - } - - ngOnInit() { - this.store.dispatch(fetchAllPublishedDataAction()); - - this.filtersSubscription = this.vm$.subscribe((vm) => { - this.router.navigate(["/publishedDatasets"], { - queryParams: { args: JSON.stringify(vm.filters) }, - }); - }); - } - - ngOnDestroy() { - this.filtersSubscription.unsubscribe(); - } -} +import { Component, OnInit, OnDestroy, Inject } from "@angular/core"; +import { Store } from "@ngrx/store"; +import { PublishedData } from "shared/sdk"; +import { Router } from "@angular/router"; +import { selectPublishedDataDashboardPageViewModel } from "state-management/selectors/published-data.selectors"; +import { CheckboxEvent } from "shared/modules/table/table.component"; +import { Subscription } from "rxjs"; + +import { MatCheckboxChange } from "@angular/material/checkbox"; +import { DOCUMENT } from "@angular/common"; +import { take } from "rxjs/operators"; +import { Message, MessageType } from "state-management/models"; +import { showMessageAction } from "state-management/actions/user.actions"; +import { Column } from "shared/modules/shared-table/shared-table.module"; +import { SciCatDataSource } from "shared/services/scicat.datasource"; +import { AppConfigService } from "app-config.service"; +import { ScicatDataService } from "shared/services/scicat-data-service"; +import { ExportExcelService } from "shared/services/export-excel.service"; +import { SelectionModel } from "@angular/cdk/collections"; + +@Component({ + selector: "app-publisheddata-dashboard", + templateUrl: "./publisheddata-dashboard.component.html", + styleUrls: ["./publisheddata-dashboard.component.scss"], +}) +export class PublisheddataDashboardComponent implements OnInit, OnDestroy { + public vm$ = this.store.select(selectPublishedDataDashboardPageViewModel); + columns: Column[] = [ + { + id: "doi", + label: "DOI", + canSort: true, + icon: "fingerprint", + matchMode: "contains", + hideOrder: 0, + }, + { + id: "title", + label: "Title", + icon: "description", + canSort: true, + matchMode: "contains", + hideOrder: 1, + }, + { + id: "creator", + label: "Creator", + icon: "face", + canSort: true, + matchMode: "contains", + hideOrder: 2, + }, + { + id: "createdBy", + icon: "account_circle", + label: "Created by", + canSort: true, + matchMode: "contains", + hideOrder: 4, + }, + { + id: "createdAt", + icon: "date_range", + label: "Created at", + format: "date", + canSort: true, + matchMode: "between", + hideOrder: 5, + }, + ]; + tableDefinition = { + collection: "publishedData", + columns: this.columns, + }; + dataSource: SciCatDataSource; + select = true; + + doiBaseUrl = "https://doi.org/"; + selectedDOIs: string[] = []; + filtersSubscription: Subscription = new Subscription(); + + constructor( + @Inject(DOCUMENT) private document: Document, + private router: Router, + private store: Store, + private appConfigService: AppConfigService, + private dataService: ScicatDataService, + private exportService: ExportExcelService + ) { + this.dataSource = new SciCatDataSource( + this.appConfigService, + this.dataService, + this.exportService, + this.tableDefinition + ); + } + + onShareClick() { + const selectionBox = this.document.createElement("textarea"); + selectionBox.style.position = "fixed"; + selectionBox.style.left = "0"; + selectionBox.style.top = "0"; + selectionBox.style.opacity = "0"; + selectionBox.value = this.selectedDOIs.join("\n"); + this.document.body.appendChild(selectionBox); + selectionBox.focus(); + selectionBox.select(); + this.document.execCommand("copy"); + this.document.body.removeChild(selectionBox); + + const message = new Message( + "The selected DOI's have been copied to your clipboard", + MessageType.Success, + 5000 + ); + this.store.dispatch(showMessageAction({ message })); + } + + onRowClick(published: PublishedData) { + const id = encodeURIComponent(published.doi); + this.router.navigateByUrl("/publishedDatasets/" + id); + } + + onSelectAll(event: { + event: MatCheckboxChange; + selection: SelectionModel; + }) { + if (event.event.checked) { + this.selectedDOIs = event.selection.selected.map( + ({ doi }) => this.doiBaseUrl + encodeURIComponent(doi) + ); + } else { + this.selectedDOIs = []; + } + } + + onSelectOne(checkboxEvent: CheckboxEvent) { + const { event, row } = checkboxEvent; + const doiUrl = this.doiBaseUrl + encodeURIComponent(row.doi); + if (event.checked) { + this.selectedDOIs.push(doiUrl); + } else { + this.selectedDOIs.splice(this.selectedDOIs.indexOf(doiUrl), 1); + } + } + + ngOnInit() { + this.filtersSubscription = this.vm$.subscribe((vm) => { + this.router.navigate(["/publishedDatasets"], { + queryParams: { args: JSON.stringify(vm.filters) }, + }); + }); + } + + ngOnDestroy() { + this.filtersSubscription.unsubscribe(); + } +} diff --git a/src/app/shared/modules/shared-table/shared-table.component.html b/src/app/shared/modules/shared-table/shared-table.component.html index bbfddb1cf..5d93475a9 100644 --- a/src/app/shared/modules/shared-table/shared-table.component.html +++ b/src/app/shared/modules/shared-table/shared-table.component.html @@ -28,6 +28,9 @@ showFirstLastButtons > + @@ -87,10 +90,31 @@ + + + + + + + + + + + + + (true, []); + // Shared Variables @Input() dataSource: SciCatDataSource; @Input() isFilesDashboard: boolean; @@ -81,7 +94,14 @@ export class SharedTableComponent @Input() pageSize = 10; @Input() pageSizeOptions: number[] = [5, 10, 25, 100]; @Input() title = ""; + @Output() rowClick = new EventEmitter(); + @Output() shareClick = new EventEmitter(); + @Output() selectAll = new EventEmitter<{ + event: MatCheckboxChange; + selection: SelectionModel; + }>(); + @Output() selectOne = new EventEmitter(); // MatTable @ViewChild(MatTable, { static: true }) dataTable: MatTable; @@ -109,6 +129,18 @@ export class SharedTableComponent ngOnInit() { this.filterForm = this.initializeFormControl(); this.subscriptions.push(this.activateColumnFilters()); + + if (this.select) { + this.columnsdef.splice(0, 0, { + id: "select", + label: "Select", + canSort: false, + icon: "checkbox", + hideFilter: true, + hideOrder: 0, + width: 50, + }); + } } initializeFormControl() { @@ -219,7 +251,6 @@ export class SharedTableComponent } loadDataPage() { - this.dataSource.loadAllData( this.filterExpressions, this.sort.active, @@ -242,6 +273,42 @@ export class SharedTableComponent this.rowClick.emit(event); } + onSelectAll(event: MatCheckboxChange) { + if (this.isAllSelected()) { + this.selection.clear(); + } else { + this.dataSource.connect().subscribe((rows) => { + rows.forEach((row) => this.selection.select(row)); + }); + } + this.selectAll.emit({ event, selection: this.selection }); + } + + onSelectOne(event: MatCheckboxChange, row: unknown) { + this.selection.toggle(row); + const selectEvent: CheckboxEvent = { + event, + row, + }; + this.selectOne.emit(selectEvent); + } + + onShare() { + this.shareClick.emit(); + } + + isAllSelected() { + const numSelected = this.selection.selected + ? this.selection.selected.length + : 0; + let numRows = 0; + this.dataSource.connect().subscribe((rows) => { + numRows = rows.length || 0; + }); + + return numSelected === numRows; + } + get visibleColumnsIds() { const visibleColumnsIds = this.visibleColumns.map((column) => column.id); @@ -338,6 +405,9 @@ export class SharedTableComponent this.columnsdef = sortedColumns.sort((a, b) => a.order - b.order); this.visibleColumns = this.columnsdef.filter((column) => column.visible); + this.withoutSelectColumns = this.select + ? this.columnsdef.filter((column) => column.visible).slice(1) + : this.columnsdef.filter((column) => column.visible); this.hiddenColumns = this.columnsdef.filter((column) => !column.visible); this.expandedElement = { filters: false }; // reset expandElement to get ride of empty row when browser increases size this.zone.run(() => { diff --git a/src/app/shared/modules/shared-table/shared-table.module.ts b/src/app/shared/modules/shared-table/shared-table.module.ts index 5f31f7589..b65fbb7a3 100644 --- a/src/app/shared/modules/shared-table/shared-table.module.ts +++ b/src/app/shared/modules/shared-table/shared-table.module.ts @@ -1,92 +1,101 @@ -import { NgModule } from "@angular/core"; -import { - CommonModule, - CurrencyPipe, - DatePipe, - DecimalPipe, - JsonPipe, - KeyValuePipe, - LowerCasePipe, - PercentPipe, - SlicePipe, - TitleCasePipe, - UpperCasePipe, -} from "@angular/common"; -import { SharedTableComponent } from "./shared-table.component"; - -import { PipesModule } from "../../pipes/pipes.module"; -import { MatCardModule } from "@angular/material/card"; -import { MatCheckboxModule } from "@angular/material/checkbox"; -import { MatIconModule } from "@angular/material/icon"; -import { MatPaginatorModule } from "@angular/material/paginator"; -import { MatSortModule, SortDirection } from "@angular/material/sort"; -import { MatTableModule } from "@angular/material/table"; -import { MatToolbarModule } from "@angular/material/toolbar"; -import { MatFormFieldModule } from "@angular/material/form-field"; -import { MatInputModule } from "@angular/material/input"; -import { MatButtonModule } from "@angular/material/button"; -import { FlexLayoutModule } from "@angular/flex-layout"; -import { FormsModule, ReactiveFormsModule } from "@angular/forms"; -import { ObjKeysPipe } from "shared/pipes/obj-keys.pipe"; -import { RouterModule } from "@angular/router"; -import { MatDatepickerModule } from "@angular/material/datepicker"; -import { MatProgressSpinnerModule } from "@angular/material/progress-spinner"; -import { DateAdapter, MAT_DATE_FORMATS, MAT_DATE_LOCALE } from "@angular/material/core"; -import { LuxonDateAdapter, MAT_LUXON_DATE_FORMATS } from "ngx-material-luxon"; -import { MatMenuModule } from "@angular/material/menu"; -export interface Column { - id: string; - type?: string; - visible?: boolean; - label: string; - hideOrder: number; - width?: number; - canSort?: boolean; - matchMode?: string; - format?: string; - icon?: string; - sortDefault?: SortDirection; - filterDefault?: any; -} - -@NgModule({ - declarations: [SharedTableComponent], - imports: [ - CommonModule, - MatCardModule, - MatCheckboxModule, - MatIconModule, - MatPaginatorModule, - MatSortModule, - MatProgressSpinnerModule, - MatTableModule, - ReactiveFormsModule, - PipesModule, - MatToolbarModule, - MatFormFieldModule, - MatInputModule, - MatButtonModule, - FlexLayoutModule, - FormsModule, - RouterModule, - MatDatepickerModule, - MatMenuModule - ], - exports: [SharedTableComponent], - providers: [ - CurrencyPipe, - DatePipe, - DecimalPipe, - JsonPipe, - ObjKeysPipe, - KeyValuePipe, - LowerCasePipe, - PercentPipe, - SlicePipe, - TitleCasePipe, - UpperCasePipe, - { provide: DateAdapter, useClass: LuxonDateAdapter, deps: [MAT_DATE_LOCALE] }, - { provide: MAT_DATE_FORMATS, useValue: MAT_LUXON_DATE_FORMATS }, - ] -}) -export class SharedTableModule { } +import { NgModule } from "@angular/core"; +import { + CommonModule, + CurrencyPipe, + DatePipe, + DecimalPipe, + JsonPipe, + KeyValuePipe, + LowerCasePipe, + PercentPipe, + SlicePipe, + TitleCasePipe, + UpperCasePipe, +} from "@angular/common"; +import { SharedTableComponent } from "./shared-table.component"; + +import { PipesModule } from "../../pipes/pipes.module"; +import { MatCardModule } from "@angular/material/card"; +import { MatCheckboxModule } from "@angular/material/checkbox"; +import { MatIconModule } from "@angular/material/icon"; +import { MatPaginatorModule } from "@angular/material/paginator"; +import { MatSortModule, SortDirection } from "@angular/material/sort"; +import { MatTableModule } from "@angular/material/table"; +import { MatToolbarModule } from "@angular/material/toolbar"; +import { MatFormFieldModule } from "@angular/material/form-field"; +import { MatInputModule } from "@angular/material/input"; +import { MatButtonModule } from "@angular/material/button"; +import { FlexLayoutModule } from "@angular/flex-layout"; +import { FormsModule, ReactiveFormsModule } from "@angular/forms"; +import { ObjKeysPipe } from "shared/pipes/obj-keys.pipe"; +import { RouterModule } from "@angular/router"; +import { MatDatepickerModule } from "@angular/material/datepicker"; +import { MatProgressSpinnerModule } from "@angular/material/progress-spinner"; +import { + DateAdapter, + MAT_DATE_FORMATS, + MAT_DATE_LOCALE, +} from "@angular/material/core"; +import { LuxonDateAdapter, MAT_LUXON_DATE_FORMATS } from "ngx-material-luxon"; +import { MatMenuModule } from "@angular/material/menu"; +export interface Column { + id: string; + type?: string; + visible?: boolean; + label: string; + hideOrder: number; + width?: number; + canSort?: boolean; + matchMode?: string; + format?: string; + icon?: string; + sortDefault?: SortDirection; + filterDefault?: any; + hideFilter?: boolean; +} + +@NgModule({ + declarations: [SharedTableComponent], + imports: [ + CommonModule, + MatCardModule, + MatCheckboxModule, + MatIconModule, + MatPaginatorModule, + MatSortModule, + MatProgressSpinnerModule, + MatTableModule, + ReactiveFormsModule, + PipesModule, + MatToolbarModule, + MatFormFieldModule, + MatInputModule, + MatButtonModule, + FlexLayoutModule, + FormsModule, + RouterModule, + MatDatepickerModule, + MatMenuModule, + ], + exports: [SharedTableComponent], + providers: [ + CurrencyPipe, + DatePipe, + DecimalPipe, + JsonPipe, + ObjKeysPipe, + KeyValuePipe, + LowerCasePipe, + PercentPipe, + SlicePipe, + TitleCasePipe, + UpperCasePipe, + { + provide: DateAdapter, + useClass: LuxonDateAdapter, + deps: [MAT_DATE_LOCALE], + }, + { provide: MAT_DATE_FORMATS, useValue: MAT_LUXON_DATE_FORMATS }, + ], +}) +export class SharedTableModule {} diff --git a/src/app/shared/services/scicat-data-service.ts b/src/app/shared/services/scicat-data-service.ts index 26aa2d195..0604004a2 100644 --- a/src/app/shared/services/scicat-data-service.ts +++ b/src/app/shared/services/scicat-data-service.ts @@ -152,6 +152,14 @@ export class ScicatDataService { .set("limits", JSON.stringify(limits)) .append("access_token", this.auth.getToken().id); + // NOTE: For published data endpoint we don't have fullquery and fullfacet and that's why it is a bit special case. + if (url.includes("publishedData")) { + return this.http.get(url, { + params, + headers: { Authorization: this.auth.getAccessTokenId() }, + }); + } + const origDatablocksFiles = url.includes("Origdatablocks") && isFilesDashboard ? "/files" : ""; @@ -179,6 +187,14 @@ export class ScicatDataService { .set("facets", JSON.stringify([])) .append("access_token", this.auth.getToken().id); + // NOTE: For published data endpoint we don't have fullquery and fullfacet and that's why it is a bit special case. + if (url.includes("publishedData")) { + return this.http.get(`${url}/count`, { + params, + headers: { Authorization: this.auth.getAccessTokenId() }, + }); + } + const origDatablocksFiles = url.includes("Origdatablocks") && isFilesDashboard ? "/files" : ""; diff --git a/src/app/shared/services/scicat.datasource.ts b/src/app/shared/services/scicat.datasource.ts index 1fcbf6eb5..297264aa5 100644 --- a/src/app/shared/services/scicat.datasource.ts +++ b/src/app/shared/services/scicat.datasource.ts @@ -76,11 +76,18 @@ export class SciCatDataSource implements DataSource { this.scicatdataService .getCount(this.url, this.columnsdef, filterExpressions, isFilesDashboard) - .subscribe((numData) => - numData[0] && numData[0].all[0] + .subscribe((numData) => { + // NOTE: For published data endpoint we don't have fullquery and fullfacet and that's why it is a bit special case. + if (this.url.includes("publishedData")) { + return numData && numData.count + ? this.countSubject.next(numData.count) + : this.countSubject.next(0); + } + + return numData[0] && numData[0].all[0] ? this.countSubject.next(numData[0].all[0].totalSets) - : this.countSubject.next(0) - ); + : this.countSubject.next(0); + }); this.scicatdataService .findAllData( @@ -140,7 +147,7 @@ export class SciCatDataSource implements DataSource { }); } - connect(collectionViewer: CollectionViewer): Observable { + connect(): Observable { return this.dataSubject.asObservable(); } From a65a4ac7c06272e108c74dd0022fee462778a0fd Mon Sep 17 00:00:00 2001 From: Martin Trajanovski Date: Thu, 8 Jun 2023 15:30:40 +0200 Subject: [PATCH 2/2] fix unit tests --- .../publisheddata-dashboard.component.spec.ts | 413 +++++++++--------- 1 file changed, 195 insertions(+), 218 deletions(-) diff --git a/src/app/publisheddata/publisheddata-dashboard/publisheddata-dashboard.component.spec.ts b/src/app/publisheddata/publisheddata-dashboard/publisheddata-dashboard.component.spec.ts index 3807685dd..b43d36c15 100644 --- a/src/app/publisheddata/publisheddata-dashboard/publisheddata-dashboard.component.spec.ts +++ b/src/app/publisheddata/publisheddata-dashboard/publisheddata-dashboard.component.spec.ts @@ -1,218 +1,195 @@ -import { - ComponentFixture, - TestBed, - inject, - waitForAsync, -} from "@angular/core/testing"; - -import { PublisheddataDashboardComponent } from "./publisheddata-dashboard.component"; -import { MockStore } from "shared/MockStubs"; -import { NO_ERRORS_SCHEMA } from "@angular/core"; -import { StoreModule, Store } from "@ngrx/store"; -import { Router } from "@angular/router"; -import { - CheckboxEvent, - PageChangeEvent, - SortChangeEvent, -} from "shared/modules/table/table.component"; -import { - changePageAction, - sortByColumnAction, -} from "state-management/actions/published-data.actions"; -import { PublishedData } from "shared/sdk"; -import { MatCheckboxChange } from "@angular/material/checkbox"; -import { of } from "rxjs"; -import { Message, MessageType } from "state-management/models"; -import { showMessageAction } from "state-management/actions/user.actions"; -import { FlexLayoutModule } from "@angular/flex-layout"; -import { MatButtonModule } from "@angular/material/button"; -import { MatIconModule } from "@angular/material/icon"; - -describe("PublisheddataDashboardComponent", () => { - let component: PublisheddataDashboardComponent; - let fixture: ComponentFixture; - - const router = { - navigateByUrl: jasmine.createSpy("navigateByUrl"), - }; - let store: MockStore; - let dispatchSpy; - - beforeEach( - waitForAsync(() => { - TestBed.configureTestingModule({ - schemas: [NO_ERRORS_SCHEMA], - imports: [ - FlexLayoutModule, - MatButtonModule, - MatIconModule, - StoreModule.forRoot({}), - ], - declarations: [PublisheddataDashboardComponent], - }); - TestBed.overrideComponent(PublisheddataDashboardComponent, { - set: { - providers: [{ provide: Router, useValue: router }], - }, - }); - TestBed.compileComponents(); - }) - ); - - beforeEach(() => { - fixture = TestBed.createComponent(PublisheddataDashboardComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - beforeEach(inject([Store], (mockStore: MockStore) => { - store = mockStore; - })); - - afterEach(() => { - fixture.destroy(); - }); - - it("should create", () => { - expect(component).toBeTruthy(); - }); - - describe("#onShareClick()", () => { - it("should copy the selected DOI's to the users clipboard and dispatch a showMessageAction", () => { - const commandSpy = spyOn(document, "execCommand"); - dispatchSpy = spyOn(store, "dispatch"); - - const message = new Message( - "The selected DOI's have been copied to your clipboard", - MessageType.Success, - 5000 - ); - - component.onShareClick(); - - expect(commandSpy).toHaveBeenCalledTimes(1); - expect(commandSpy).toHaveBeenCalledWith("copy"); - expect(dispatchSpy).toHaveBeenCalledTimes(1); - expect(dispatchSpy).toHaveBeenCalledWith(showMessageAction({ message })); - }); - }); - - describe("#onPageChange()", () => { - it("should dispatch a changePageAction action", () => { - dispatchSpy = spyOn(store, "dispatch"); - - const event: PageChangeEvent = { - pageIndex: 0, - pageSize: 25, - length: 25, - }; - component.onPageChange(event); - - expect(dispatchSpy).toHaveBeenCalledTimes(1); - expect(dispatchSpy).toHaveBeenCalledWith( - changePageAction({ page: event.pageIndex, limit: event.pageSize }) - ); - }); - }); - - describe("#onSortChange()", () => { - it("should dispatch a sortByColumnAction", () => { - dispatchSpy = spyOn(store, "dispatch"); - - const event: SortChangeEvent = { - active: "title", - direction: "asc", - }; - component.onSortChange(event); - - expect(dispatchSpy).toHaveBeenCalledTimes(1); - expect(dispatchSpy).toHaveBeenCalledWith( - sortByColumnAction({ column: event.active, direction: event.direction }) - ); - }); - }); - - describe("#onRowClick", () => { - it("should navigate to a Published Dataset", () => { - const published = new PublishedData(); - const id = encodeURIComponent(published.doi); - component.onRowClick(published); - - expect(router.navigateByUrl).toHaveBeenCalledTimes(1); - expect(router.navigateByUrl).toHaveBeenCalledWith( - "/publishedDatasets/" + id - ); - }); - }); - - describe("#onSelectAll()", () => { - it("should add all DOI's to selectedDOIs if checked is true", () => { - const published = new PublishedData({ - doi: "test", - creator: ["test"], - publisher: "test", - publicationYear: 2021, - title: "test", - abstract: "test", - dataDescription: "test", - resourceType: "test", - pidArray: [], - }); - - spyOn(component.vm$, "pipe").and.returnValue( - of({ publishedData: [published] }) - ); - - const event = { - checked: true, - } as MatCheckboxChange; - - component.onSelectAll(event); - - expect(component.selectedDOIs.length).toEqual(1); - }); - - it("should remove all DOI's from selectedDOIs if checked is false", () => { - component.selectedDOIs.push( - component.doiBaseUrl + "test1", - component.doiBaseUrl + "test2" - ); - - const event = { - checked: false, - } as MatCheckboxChange; - - component.onSelectAll(event); - - expect(component.selectedDOIs.length).toEqual(0); - }); - }); - - describe("#onSelectOne()", () => { - it("should add the selected DOI to selectedDOIs if checked is true", () => { - const checkboxEvent: CheckboxEvent = { - event: { checked: true } as MatCheckboxChange, - row: { doi: "test" }, - }; - - component.onSelectOne(checkboxEvent); - - expect(component.selectedDOIs).toContain( - component.doiBaseUrl + checkboxEvent.row.doi - ); - }); - - it("should remove the deselected DOI from selectedDOIs if checked is false", () => { - const checkboxEvent: CheckboxEvent = { - event: { checked: false } as MatCheckboxChange, - row: { doi: "test" }, - }; - - component.selectedDOIs.push(component.doiBaseUrl + checkboxEvent.row.doi); - - component.onSelectOne(checkboxEvent); - - expect(component.selectedDOIs.length).toEqual(0); - }); - }); -}); +import { + ComponentFixture, + TestBed, + inject, + waitForAsync, +} from "@angular/core/testing"; + +import { PublisheddataDashboardComponent } from "./publisheddata-dashboard.component"; +import { MockStore } from "shared/MockStubs"; +import { NO_ERRORS_SCHEMA } from "@angular/core"; +import { StoreModule, Store } from "@ngrx/store"; +import { Router } from "@angular/router"; +import { CheckboxEvent } from "shared/modules/table/table.component"; +import { PublishedData } from "shared/sdk"; +import { MatCheckboxChange } from "@angular/material/checkbox"; +import { of } from "rxjs"; +import { Message, MessageType } from "state-management/models"; +import { showMessageAction } from "state-management/actions/user.actions"; +import { FlexLayoutModule } from "@angular/flex-layout"; +import { MatButtonModule } from "@angular/material/button"; +import { MatIconModule } from "@angular/material/icon"; +import { AppConfigService } from "app-config.service"; +import { ScicatDataService } from "shared/services/scicat-data-service"; +import { ExportExcelService } from "shared/services/export-excel.service"; + +const getConfig = () => ({}); + +describe("PublisheddataDashboardComponent", () => { + let component: PublisheddataDashboardComponent; + let fixture: ComponentFixture; + + const router = { + navigateByUrl: jasmine.createSpy("navigateByUrl"), + }; + let store: MockStore; + let dispatchSpy; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + schemas: [NO_ERRORS_SCHEMA], + imports: [ + FlexLayoutModule, + MatButtonModule, + MatIconModule, + StoreModule.forRoot({}), + ], + declarations: [PublisheddataDashboardComponent], + }); + TestBed.overrideComponent(PublisheddataDashboardComponent, { + set: { + providers: [ + { + provide: Router, + useValue: router, + }, + { + provide: AppConfigService, + useValue: { getConfig }, + }, + { provide: ScicatDataService, useValue: {} }, + { provide: ExportExcelService, useValue: {} }, + ], + }, + }); + TestBed.compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(PublisheddataDashboardComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + beforeEach(inject([Store], (mockStore: MockStore) => { + store = mockStore; + })); + + afterEach(() => { + fixture.destroy(); + }); + + it("should create", () => { + expect(component).toBeTruthy(); + }); + + describe("#onShareClick()", () => { + it("should copy the selected DOI's to the users clipboard and dispatch a showMessageAction", () => { + const commandSpy = spyOn(document, "execCommand"); + dispatchSpy = spyOn(store, "dispatch"); + + const message = new Message( + "The selected DOI's have been copied to your clipboard", + MessageType.Success, + 5000 + ); + + component.onShareClick(); + + expect(commandSpy).toHaveBeenCalledTimes(1); + expect(commandSpy).toHaveBeenCalledWith("copy"); + expect(dispatchSpy).toHaveBeenCalledTimes(1); + expect(dispatchSpy).toHaveBeenCalledWith(showMessageAction({ message })); + }); + }); + + describe("#onRowClick", () => { + it("should navigate to a Published Dataset", () => { + const published = new PublishedData(); + const id = encodeURIComponent(published.doi); + component.onRowClick(published); + + expect(router.navigateByUrl).toHaveBeenCalledTimes(1); + expect(router.navigateByUrl).toHaveBeenCalledWith( + "/publishedDatasets/" + id + ); + }); + }); + + describe("#onSelectAll()", () => { + it("should add all DOI's to selectedDOIs if checked is true", () => { + const published = new PublishedData({ + doi: "test", + creator: ["test"], + publisher: "test", + publicationYear: 2021, + title: "test", + abstract: "test", + dataDescription: "test", + resourceType: "test", + pidArray: [], + }); + + spyOn(component.vm$, "pipe").and.returnValue( + of({ publishedData: [published] }) + ); + + const event = { + event: { + checked: true, + }, + selection: { selected: [published] }, + }; + + component.onSelectAll(event as any); + + expect(component.selectedDOIs.length).toEqual(1); + }); + + it("should remove all DOI's from selectedDOIs if checked is false", () => { + component.selectedDOIs.push( + component.doiBaseUrl + "test1", + component.doiBaseUrl + "test2" + ); + + const event = { + event: { + checked: false, + }, + selection: [], + }; + + component.onSelectAll(event as any); + + expect(component.selectedDOIs.length).toEqual(0); + }); + }); + + describe("#onSelectOne()", () => { + it("should add the selected DOI to selectedDOIs if checked is true", () => { + const checkboxEvent: CheckboxEvent = { + event: { checked: true } as MatCheckboxChange, + row: { doi: "test" }, + }; + + component.onSelectOne(checkboxEvent); + + expect(component.selectedDOIs).toContain( + component.doiBaseUrl + checkboxEvent.row.doi + ); + }); + + it("should remove the deselected DOI from selectedDOIs if checked is false", () => { + const checkboxEvent: CheckboxEvent = { + event: { checked: false } as MatCheckboxChange, + row: { doi: "test" }, + }; + + component.selectedDOIs.push(component.doiBaseUrl + checkboxEvent.row.doi); + + component.onSelectOne(checkboxEvent); + + expect(component.selectedDOIs.length).toEqual(0); + }); + }); +});