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 b7a2aac91c4..8cc2aaee145 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 093c15e4251..d8b39dd7888 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
@@ -16,8 +16,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 { NgbModal } from '@ng-bootstrap/ng-bootstrap';
+import { NgbActiveModal, NgbModal, NgbTooltip } from '@ng-bootstrap/ng-bootstrap';
import { HostWindowService } from '../../shared/host-window.service';
import { HostWindowServiceStub } from '../../shared/testing/host-window-service.stub';
import {
@@ -67,6 +66,7 @@ describe('ClarinLicenseTableComponent', () => {
findAll: mockLicenseRD$,
create: createdLicenseRD$,
put: createdLicenseRD$,
+ delete: createNoContentRemoteDataObject$(),
searchBy: mockLicenseRD$,
getLinkPath: observableOf('')
});
@@ -222,6 +222,55 @@ describe('ClarinLicenseTableComponent', () => {
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));
+ });
+ });
+
describe('label edit flow', () => {
beforeEach(() => {
notificationService.success.calls.reset();
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 effe2774ec7..91e0457f18e 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';
@@ -356,7 +356,7 @@ export class ClarinLicenseTableComponent implements OnInit, OnDestroy {
* 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))
@@ -369,6 +369,13 @@ export class ClarinLicenseTableComponent implements OnInit, OnDestroy {
});
}
+ /**
+ * Returns whether selected license has attached bitstreams.
+ */
+ isSelectedLicenseInUse(): boolean {
+ return this.selectedLicense?.bitstreams > 0;
+ }
+
/**
* Open the edit modal for the selected license label, pre-filling its current values.
* On confirm, calls the PUT service and refreshes the label list.
diff --git a/src/assets/i18n/cs.json5 b/src/assets/i18n/cs.json5
index 1a1f63ac3b0..22fba6a2f83 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 44965fbe71a..293aac9bcb6 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 ...",