diff --git a/src/app/clarin-licenses/clarin-license-table/clarin-license-table.component.html b/src/app/clarin-licenses/clarin-license-table/clarin-license-table.component.html index 729b0f781d5..a74f314a7cc 100644 --- a/src/app/clarin-licenses/clarin-license-table/clarin-license-table.component.html +++ b/src/app/clarin-licenses/clarin-license-table/clarin-license-table.component.html @@ -76,9 +76,21 @@
- + + +
diff --git a/src/app/clarin-licenses/clarin-license-table/clarin-license-table.component.spec.ts b/src/app/clarin-licenses/clarin-license-table/clarin-license-table.component.spec.ts index 0cc824bdd8b..1da35adf950 100644 --- a/src/app/clarin-licenses/clarin-license-table/clarin-license-table.component.spec.ts +++ b/src/app/clarin-licenses/clarin-license-table/clarin-license-table.component.spec.ts @@ -1,4 +1,5 @@ import { ComponentFixture, fakeAsync, TestBed } from '@angular/core/testing'; +import { By } from '@angular/platform-browser'; import { ClarinLicenseTableComponent } from './clarin-license-table.component'; import { NotificationsServiceStub } from '../../shared/testing/notifications-service.stub'; import { ClarinLicenseDataService } from '../../core/data/clarin/clarin-license-data.service'; @@ -14,7 +15,7 @@ import { PaginationServiceStub } from '../../shared/testing/pagination-service.s import { NotificationsService } from '../../shared/notifications/notifications.service'; import { defaultPagination } from '../clarin-license-table-pagination'; import { ClarinLicenseLabelDataService } from '../../core/data/clarin/clarin-license-label-data.service'; -import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; +import { NgbActiveModal, NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; import { HostWindowService } from '../../shared/host-window.service'; import { HostWindowServiceStub } from '../../shared/testing/host-window-service.stub'; import { @@ -25,7 +26,7 @@ import { mockNonExtendedLicenseLabel, successfulResponse } from '../../shared/testing/clarin-license-mock'; import {GroupDataService} from '../../core/eperson/group-data.service'; -import {createSuccessfulRemoteDataObject$} from '../../shared/remote-data.utils'; +import { createNoContentRemoteDataObject$, createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils'; import {createPaginatedList} from '../../shared/testing/utils.test'; import {LinkHeadService} from '../../core/services/link-head.service'; import {ConfigurationDataService} from '../../core/data/configuration-data.service'; @@ -51,6 +52,7 @@ describe('ClarinLicenseTableComponent', () => { findAll: mockLicenseRD$, create: createdLicenseRD$, put: createdLicenseRD$, + delete: createNoContentRemoteDataObject$(), searchBy: mockLicenseRD$, getLinkPath: observableOf('') }); @@ -181,4 +183,53 @@ describe('ClarinLicenseTableComponent', () => { expect((component as any).clarinLicenseService.searchBy).toHaveBeenCalled(); expect((component as ClarinLicenseTableComponent).licensesRD$).not.toBeNull(); }); + + describe('license delete button', () => { + const getDeleteControls = () => { + const actionsRow = fixture.debugElement.query(By.css('.mt-2')); + const deleteWrapper = actionsRow.query(By.css('.btn-group.pr-1:last-child span')); + const deleteButton = deleteWrapper.query(By.css('button.btn-danger')); + return { deleteWrapper, deleteButton }; + }; + + beforeEach(() => { + (clarinLicenseDataService.delete as jasmine.Spy).calls.reset(); + }); + + it('should disable delete button and expose tooltip when selected license has bitstreams', () => { + component.selectedLicense = Object.assign({}, mockLicense, { bitstreams: 2 }); + fixture.detectChanges(); + + const { deleteWrapper, deleteButton } = getDeleteControls(); + const deleteTooltip = deleteWrapper.injector.get(NgbTooltip); + + expect(deleteButton.attributes['aria-disabled']).toBe('true'); + expect(deleteButton.nativeElement.classList.contains('disabled')).toBeTrue(); + expect((deleteWrapper.nativeElement as HTMLElement).getAttribute('tabindex')).toBe('0'); + expect(deleteTooltip.ngbTooltip as string).toContain('clarin-license.button.delete-l'); + }); + + it('should not call delete when clicking disabled delete button', () => { + component.selectedLicense = Object.assign({}, mockLicense, { bitstreams: 1 }); + fixture.detectChanges(); + + const { deleteButton } = getDeleteControls(); + deleteButton.nativeElement.click(); + + expect((clarinLicenseDataService.delete as jasmine.Spy)).not.toHaveBeenCalled(); + }); + + it('should enable delete button and call delete when selected license has no bitstreams', () => { + component.selectedLicense = Object.assign({}, mockLicense, { bitstreams: 0 }); + fixture.detectChanges(); + + const { deleteWrapper, deleteButton } = getDeleteControls(); + deleteButton.nativeElement.click(); + + expect(deleteButton.attributes['aria-disabled']).toBe('false'); + expect(deleteButton.nativeElement.classList.contains('disabled')).toBeFalse(); + expect((deleteWrapper.nativeElement as HTMLElement).getAttribute('tabindex')).toBeNull(); + expect((clarinLicenseDataService.delete as jasmine.Spy)).toHaveBeenCalledWith(String(mockLicense.id)); + }); + }); }); diff --git a/src/app/clarin-licenses/clarin-license-table/clarin-license-table.component.ts b/src/app/clarin-licenses/clarin-license-table/clarin-license-table.component.ts index 143f991aab1..d60411c7ae3 100644 --- a/src/app/clarin-licenses/clarin-license-table/clarin-license-table.component.ts +++ b/src/app/clarin-licenses/clarin-license-table/clarin-license-table.component.ts @@ -15,7 +15,7 @@ import { DefineLicenseLabelFormComponent } from './modal/define-license-label-fo import { ClarinLicenseConfirmationSerializer } from '../../core/shared/clarin/clarin-license-confirmation-serializer'; import { NotificationsService } from '../../shared/notifications/notifications.service'; import { TranslateService } from '@ngx-translate/core'; -import { isNull } from '../../shared/empty.util'; +import { hasNoValue, isNull } from '../../shared/empty.util'; import { ClarinLicenseLabel } from '../../core/shared/clarin/clarin-license-label.model'; import { ClarinLicenseLabelDataService } from '../../core/data/clarin/clarin-license-label-data.service'; import { ClarinLicenseLabelExtendedSerializer } from '../../core/shared/clarin/clarin-license-label-extended-serializer'; @@ -274,7 +274,7 @@ export class ClarinLicenseTableComponent implements OnInit { * Delete selected license. If none license is selected do nothing. */ deleteLicense() { - if (isNull(this.selectedLicense?.id)) { + if (hasNoValue(this.selectedLicense?.id) || this.isSelectedLicenseInUse()) { return; } this.clarinLicenseService.delete(String(this.selectedLicense.id)) @@ -287,6 +287,13 @@ export class ClarinLicenseTableComponent implements OnInit { }); } + /** + * Returns whether selected license has attached bitstreams. + */ + isSelectedLicenseInUse(): boolean { + return this.selectedLicense?.bitstreams > 0; + } + /** * Pop up the notification about the request success. Messages are loaded from the `en.json5`. * @param operationResponse current response diff --git a/src/assets/i18n/cs.json5 b/src/assets/i18n/cs.json5 index ac5476fd0db..b287088939a 100644 --- a/src/assets/i18n/cs.json5 +++ b/src/assets/i18n/cs.json5 @@ -9423,6 +9423,9 @@ // "clarin-license.button.delete-license": "Delete License", "clarin-license.button.delete-license": "Odstranit licenci", + // "clarin-license.button.delete-license.disabled-tooltip": "License \"{{name}}\" cannot be deleted because it is attached to one or more bitstreams.", + "clarin-license.button.delete-license.disabled-tooltip": "Licenci \"{{name}}\" nelze smazat, protože jsou k ní připojeny jeden nebo více bitstreamů.", + // "clarin-license.button.search": "Search", "clarin-license.button.search": "Hledat", diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index 472f95c978e..e0ded51faf7 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -6245,6 +6245,8 @@ "clarin-license.button.delete-license": "Delete License", + "clarin-license.button.delete-license.disabled-tooltip": "License \"{{name}}\" cannot be deleted because it is attached to one or more bitstreams.", + "clarin-license.button.search": "Search", "clarin-license.button.search.placeholder": "Search by the license name ...",