Skip to content
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@

<div class="d-flex flex-row">
<div class="font-weight-bold {{ field.id }}">
{{ field.label }}
{{ field.label | translate }}
</div>
<button
class="btn btn-outline-info btn-sm ml-2"
Expand All @@ -59,9 +59,9 @@
<div class="modal-header mb-4 ">
<div>
<h4>
{{ field.label }}
{{ field.label | translate }}
</h4>
<div [innerHTML]="field.description"></div>
<div [innerHTML]="field.description | translate"></div>
</div>
<button type="button" class="close"
(click)="closeInfoModal()" aria-label="Close">
Expand All @@ -73,9 +73,9 @@ <h4>
<div *ngFor="let value of field.enums"
class="mb-4">
<h5>
{{ value.label }}
{{ value.label | translate }}
</h5>
<div [innerHTML]="value.description" class="font-weight-light"></div>
<div [innerHTML]="value.description | translate" class="font-weight-light"></div>
</div>
</div>

Expand All @@ -86,7 +86,7 @@ <h5>
<ds-select *ngIf="field.enums?.length > 5" [disabled]="field.id === 'jurisdiction' && defaultJurisdiction !== undefined && defaultJurisdiction !== 'none'">
<ng-container class="selection" *ngVar="getSelectedOption(getSelectedCcLicense(), field) as option">
<span *ngIf="option">
{{ option.label }}
{{ option.label | translate }}
</span>
<span *ngIf="!option">
{{ 'submission.sections.ccLicense.option.select' | translate }}
Expand All @@ -97,7 +97,7 @@ <h5>
<button *ngFor="let option of field.enums"
class="dropdown-item"
(click)="selectOption(getSelectedCcLicense(), field, option)">
{{ option.label }}
{{ option.label | translate }}
</button>
</div>
</ng-container>
Expand All @@ -108,10 +108,10 @@ <h5>
class="d-flex flex-row m-1">
<div (click)="selectOption(getSelectedCcLicense(), field, option)">
<input type="radio"
title="{{ option.label }}"
title="{{ option.label | translate }}"
class="mr-1"
[checked]="isSelectedOption(getSelectedCcLicense(), field, option)">
<span>{{ option.label }}</span>
<span>{{ option.label | translate }}</span>
</div>
</div>
</ng-container>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,12 @@
}

.ccLicense-select {
width: fit-content;
width: 400px;
max-width: 100%;

ngb-dropdown, .input-group, input.form-control {
width: 100%;
}
}

.scrollable-menu {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
import { SubmissionSectionCcLicensesComponent } from './submission-section-cc-licenses.component';
import { SUBMISSION_CC_LICENSE } from '../../../core/submission/models/submission-cc-licence.resource-type';
import { of as observableOf } from 'rxjs';
import { of as observableOf, Subject } from 'rxjs';
import { SubmissionCcLicenseDataService } from '../../../core/submission/submission-cc-license-data.service';
import { DebugElement } from '@angular/core';
import { By } from '@angular/platform-browser';
Expand Down Expand Up @@ -139,9 +139,11 @@ describe('SubmissionSectionCcLicensesComponent', () => {
getCcLicenseLink: observableOf('test cc license link'),
});

let sectionState$: Subject<any>;

const sectionService = {
getSectionState: () => {
return observableOf({});
return sectionState$;
},
setSectionStatus: () => undefined,
updateSectionData: (submissionId, sectionId, updatedData) => {
Expand Down Expand Up @@ -186,6 +188,7 @@ describe('SubmissionSectionCcLicensesComponent', () => {
}));

beforeEach(() => {
sectionState$ = new Subject<any>();
fixture = TestBed.createComponent(SubmissionSectionCcLicensesComponent);
component = fixture.componentInstance;
de = fixture.debugElement;
Expand Down Expand Up @@ -281,4 +284,168 @@ describe('SubmissionSectionCcLicensesComponent', () => {
});
});
});

describe('when a license uri is stored', () => {

const enumOption = (id: string) => ({ id, label: id, description: id });
const _links = { self: { href: 'test link' } };
const cc30: SubmissionCcLicence = {
id: 'cc30',
type: SUBMISSION_CC_LICENSE,
name: 'Creative Commons License (3.0)',
fields: [
{ id: 'commercial', label: 'commercial', description: '', enums: [enumOption('y'), enumOption('n')] },
{ id: 'derivatives', label: 'derivatives', description: '', enums: [enumOption('y'), enumOption('sa'), enumOption('n')] },
{ id: 'jurisdiction', label: 'jurisdiction', description: '', enums: [enumOption(''), enumOption('us')] },
],
_links,
};
const cc40: SubmissionCcLicence = {
id: 'cc40',
type: SUBMISSION_CC_LICENSE,
name: 'Creative Commons License (4.0)',
fields: [
{ id: 'commercial', label: 'commercial', description: '', enums: [enumOption('y'), enumOption('n')] },
{ id: 'derivatives', label: 'derivatives', description: '', enums: [enumOption('y'), enumOption('sa'), enumOption('n')] },
],
_links,
};
const cc0: SubmissionCcLicence = { id: 'cc0', type: SUBMISSION_CC_LICENSE, name: 'CC0 1.0 Universal', fields: [], _links };
const mark: SubmissionCcLicence = { id: 'mark', type: SUBMISSION_CC_LICENSE, name: 'Public Domain Mark 1.0 Universal', fields: [], _links };
const sampling: SubmissionCcLicence = {
id: 'sampling',
type: SUBMISSION_CC_LICENSE,
name: 'Creative Commons Sampling License (1.0)',
fields: [
{ id: 'sampling', label: 'sampling', description: '', enums: [enumOption('sampling'), enumOption('samplingplus'), enumOption('ncsamplingplus')] },
],
_links,
};

const restore = (uri: string) => {
component.submissionCcLicenses = [cc30, cc40, cc0, mark, sampling];
component.sectionData.data = { uri };
(component as any).restoreSelectionFromStoredUri();
};

it('should restore a CC 4.0 license with its options', () => {
restore('https://creativecommons.org/licenses/by-nc-sa/4.0/');
expect(component.data.ccLicense.id).toEqual('cc40');
expect(component.data.ccLicense.fields.commercial.id).toEqual('n');
expect(component.data.ccLicense.fields.derivatives.id).toEqual('sa');
expect(component.data.uri).toEqual('https://creativecommons.org/licenses/by-nc-sa/4.0/');
});

it('should restore a CC 3.0 license with its jurisdiction', () => {
restore('https://creativecommons.org/licenses/by-nd/3.0/us/');
expect(component.data.ccLicense.id).toEqual('cc30');
expect(component.data.ccLicense.fields.commercial.id).toEqual('y');
expect(component.data.ccLicense.fields.derivatives.id).toEqual('n');
expect(component.data.ccLicense.fields.jurisdiction.id).toEqual('us');
});

it('should restore a Sampling license with its option', () => {
restore('https://creativecommons.org/licenses/nc-sampling+/1.0/');
expect(component.data.ccLicense.id).toEqual('sampling');
expect(component.data.ccLicense.fields.sampling.id).toEqual('ncsamplingplus');
});

it('should distinguish public domain licenses with the same version', () => {
restore('https://creativecommons.org/publicdomain/mark/1.0/');
expect(component.data.ccLicense.id).toEqual('mark');
restore('https://creativecommons.org/publicdomain/zero/1.0/');
expect(component.data.ccLicense.id).toEqual('cc0');
});

it('should leave an unknown uri untouched', () => {
restore('https://example.org/my-license');
expect(component.data.ccLicense).toBeUndefined();
expect(component.accepted).toBeTrue();
});

it('should not override an existing selection', () => {
component.submissionCcLicenses = [cc30, cc40, cc0, mark];
component.sectionData.data = { uri: 'https://creativecommons.org/licenses/by/4.0/', ccLicense: { id: 'cc0', fields: {} } };
(component as any).restoreSelectionFromStoredUri();
expect(component.data.ccLicense.id).toEqual('cc0');
});
});

describe('when the section state changes', () => {
const uri = 'https://creativecommons.org/licenses/by/4.0/';

beforeEach(() => {
operationsBuilder.add.calls.reset();
operationsBuilder.remove.calls.reset();
});

it('should queue a remove when the stored license is no longer accepted', () => {
component.sectionData.data = { uri };
sectionState$.next({ data: { uri, accepted: false } });
expect(operationsBuilder.remove).toHaveBeenCalledTimes(1);
});

it('should queue the stored license again when it is unticked and ticked again before saving', () => {
component.sectionData.data = { uri };
sectionState$.next({ data: { uri, accepted: false } });
sectionState$.next({ data: { uri, accepted: true } });
expect(operationsBuilder.remove).toHaveBeenCalledTimes(1);
expect(operationsBuilder.add).toHaveBeenCalledTimes(1);
});

describe('when the stored license is selected', () => {
const cc0Uri = 'https://creativecommons.org/publicdomain/zero/1.0/';
const cc0License: SubmissionCcLicence = {
id: 'cc0', type: SUBMISSION_CC_LICENSE, name: 'CC0 1.0 Universal', fields: [], _links: submissionCcLicenses[0]._links,
};

beforeEach(() => {
component.submissionCcLicenses = [cc0License];
submissionCcLicenseUrlDataService.getCcLicenseLink.calls.reset();
});

it('should queue the stored uri right away when it is ticked again before saving', () => {
component.sectionData.data = { uri: cc0Uri, ccLicense: { id: 'cc0', fields: {} } };
sectionState$.next({ data: { uri: cc0Uri, ccLicense: { id: 'cc0', fields: {} }, accepted: false } });
sectionState$.next({ data: { uri: cc0Uri, ccLicense: { id: 'cc0', fields: {} }, accepted: true } });
expect(operationsBuilder.add).toHaveBeenCalledOnceWith(jasmine.anything(), cc0Uri, false, true);
expect(submissionCcLicenseUrlDataService.getCcLicenseLink).not.toHaveBeenCalled();
});

it('should look up the link when the selection differs from the stored uri', () => {
component.sectionData.data = { uri, ccLicense: { id: 'cc0', fields: {} } };
sectionState$.next({ data: { uri, ccLicense: { id: 'cc0', fields: {} }, accepted: false } });
sectionState$.next({ data: { uri, ccLicense: { id: 'cc0', fields: {} }, accepted: true } });
expect(operationsBuilder.add).toHaveBeenCalledOnceWith(jasmine.anything(), 'test cc license link', false, true);
});
});

it('should not queue a duplicate add for a license the server already stores', () => {
component.sectionData.data = { uri };
sectionState$.next({ data: { uri, accepted: false } });
// Section data from the server, without an accepted state, ends the pending remove
sectionState$.next({ data: { uri } });
sectionState$.next({ data: { uri, accepted: true } });
expect(operationsBuilder.add).not.toHaveBeenCalled();
});

it('should not queue a remove for a save response of an already removed license', () => {
component.sectionData.data = { uri, accepted: false };
// The REST API returns the cleared section data without an accepted state
sectionState$.next({ data: { uri: null, rights: null, file: null } });
expect(operationsBuilder.remove).not.toHaveBeenCalled();
expect(component.accepted).toBeFalse();
});

it('should queue a single remove when another license is selected', () => {
// Like the store, only apply the update once it is emitted by the section state
let updatedData;
spyOn(sectionService, 'updateSectionData').and.callFake((submissionId, sectionId, data) => updatedData = data);
component.submissionCcLicenses = submissionCcLicenses;
component.sectionData.data = { uri };
component.selectCcLicense(submissionCcLicenses[0]);
sectionState$.next({ data: updatedData });
expect(operationsBuilder.remove).toHaveBeenCalledTimes(1);
});
});
});
Loading
Loading