Skip to content

Commit 4427ec2

Browse files
Merged ux-plus-cris into task/ux-plus-cris/UXP-87
2 parents 7a8e667 + 405166d commit 4427ec2

36 files changed

+811
-252
lines changed

src/app/core/services/location.service.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -184,8 +184,8 @@ export class LocationService {
184184
public isValidCoordinateString(coordinateString: string): boolean {
185185
if (this.isDecimalCoordinateString(coordinateString)) {
186186
const coordinateArray = coordinateString.split(',');
187-
const latitude = parseFloat(coordinateArray[0]);
188-
const longitude = parseFloat(coordinateArray[1]);
187+
const latitude = parseFloat(coordinateArray[0].trim());
188+
const longitude = parseFloat(coordinateArray[1].trim());
189189
return !isNaN(latitude) && !isNaN(longitude) && this.isValidDecimalCoordinatePair(latitude, longitude);
190190
} else {
191191
return false;

src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/field-rendering-type.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ export enum FieldRenderingType {
77
IDENTIFIER = 'IDENTIFIER',
88
CRISREF = 'CRISREF',
99
THUMBNAIL = 'THUMBNAIL',
10+
IMAGE = 'IMAGE',
1011
ATTACHMENT = 'ATTACHMENT',
1112
TABLE = 'TABLE',
1213
INLINE = 'INLINE',
@@ -15,16 +16,17 @@ export enum FieldRenderingType {
1516
VALUEPAIR = 'VALUEPAIR',
1617
HTML = 'HTML',
1718
LONGHTML = 'LONGHTML',
18-
ADVANCEDATTACHMENT = 'ADVANCEDATTACHMENT',
19-
SIMPLEATTACHMENT = 'SIMPLEATTACHMENT',
20-
AUTHORITYLINK = 'AUTHORITYLINK',
2119
GMAP = 'GOOGLEMAPS',
2220
OSMAP = 'OPENSTREETMAP',
2321
GMAPGROUP = 'GOOGLEMAPS-GROUP',
2422
OSMAPGROUP = 'OPENSTREETMAP-GROUP',
2523
BROWSE = 'BROWSE',
2624
TAGBROWSE = 'TAG-BROWSE',
2725
MARKDOWN = 'MARKDOWN',
26+
LONGMARKDOWN = 'LONGMARKDOWN',
2827
SEARCH = 'SEARCH',
2928
TAGSEARCH = 'TAG-SEARCH',
29+
ADVANCEDATTACHMENT = 'ADVANCEDATTACHMENT',
30+
SIMPLEATTACHMENT = 'SIMPLEATTACHMENT',
31+
AUTHORITYLINK = 'AUTHORITYLINK',
3032
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<div [class]="field.styleValue">
2+
<img *ngIf="(imageUrl$ | async) as url" [src]="url">
3+
</div>
4+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
img {
2+
width: 100%;
3+
max-width: 900px;
4+
max-height: 600px;
5+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
import {
2+
ComponentFixture,
3+
TestBed,
4+
} from '@angular/core/testing';
5+
import {
6+
TranslateLoader,
7+
TranslateModule,
8+
TranslateService,
9+
} from '@ngx-translate/core';
10+
import {
11+
Observable,
12+
of,
13+
} from 'rxjs';
14+
15+
import { BitstreamDataService } from '../../../../../../../core/data/bitstream-data.service';
16+
import { RemoteData } from '../../../../../../../core/data/remote-data';
17+
import { LayoutField } from '../../../../../../../core/layout/models/box.model';
18+
import { Bitstream } from '../../../../../../../core/shared/bitstream.model';
19+
import { Item } from '../../../../../../../core/shared/item.model';
20+
import { TranslateLoaderMock } from '../../../../../../../shared/mocks/translate-loader.mock';
21+
import { createSuccessfulRemoteDataObject$ } from '../../../../../../../shared/remote-data.utils';
22+
import { createPaginatedList } from '../../../../../../../shared/testing/utils.test';
23+
import { FieldRenderingType } from '../../../../boxes/metadata/rendering-types/field-rendering-type';
24+
import { ImageComponent } from './image.component';
25+
26+
describe('ImageComponent', () => {
27+
let component: ImageComponent;
28+
let fixture: ComponentFixture<ImageComponent>;
29+
30+
const mockField: LayoutField = {
31+
metadata: '',
32+
label: 'Files',
33+
rendering: FieldRenderingType.IMAGE,
34+
fieldType: 'BITSTREAM',
35+
style: null,
36+
styleLabel: 'test-style-label',
37+
styleValue: 'test-style-value',
38+
labelAsHeading: false,
39+
valuesInline: true,
40+
bitstream: {
41+
bundle: 'ORIGINAL',
42+
metadataField: null,
43+
metadataValue: null,
44+
},
45+
};
46+
47+
const testItem = Object.assign(new Item(), {
48+
bundles: of({}),
49+
metadata: {
50+
'dc.identifier.doi': [
51+
{
52+
value: 'doi:10.1392/dironix',
53+
},
54+
],
55+
},
56+
_links: {
57+
self: { href: 'obj-selflink' },
58+
},
59+
uuid: 'item-uuid',
60+
});
61+
62+
const mockBitstreamDataService: any = jasmine.createSpyObj('BitstreamDataService', {
63+
getThumbnailFor(item: Item): Observable<RemoteData<Bitstream>> {
64+
return createSuccessfulRemoteDataObject$(new Bitstream());
65+
},
66+
findAllByItemAndBundleName: jasmine.createSpy('findAllByItemAndBundleName'),
67+
findByItem: of(createPaginatedList([new Bitstream()])),
68+
});
69+
const langList = ['en', 'xx', 'de'];
70+
const translateServiceStub: any = {
71+
getLangs: () => {
72+
return langList;
73+
},
74+
getBrowserLang: () => {
75+
return langList;
76+
},
77+
// eslint-disable-next-line @typescript-eslint/no-empty-function
78+
use: (param: string) => {
79+
},
80+
};
81+
82+
beforeEach(async () => {
83+
await TestBed.configureTestingModule({
84+
imports: [
85+
ImageComponent,
86+
TranslateModule.forRoot({
87+
loader: {
88+
provide: TranslateLoader,
89+
useClass: TranslateLoaderMock,
90+
},
91+
}),
92+
],
93+
providers: [
94+
{ provide: 'fieldProvider', useValue: mockField },
95+
{ provide: 'itemProvider', useValue: testItem },
96+
{ provide: 'renderingSubTypeProvider', useValue: '' },
97+
{ provide: 'tabNameProvider', useValue: '' },
98+
{ provide: BitstreamDataService, useValue: mockBitstreamDataService },
99+
{ provide: TranslateService, useValue: translateServiceStub },
100+
],
101+
})
102+
.compileComponents();
103+
});
104+
105+
beforeEach(() => {
106+
fixture = TestBed.createComponent(ImageComponent);
107+
component = fixture.componentInstance;
108+
fixture.detectChanges();
109+
});
110+
111+
it('should create', () => {
112+
expect(component).toBeTruthy();
113+
});
114+
});
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import { AsyncPipe } from '@angular/common';
2+
import {
3+
Component,
4+
OnInit,
5+
} from '@angular/core';
6+
import {
7+
BehaviorSubject,
8+
Observable,
9+
} from 'rxjs';
10+
import { map } from 'rxjs/operators';
11+
12+
import { Bitstream } from '../../../../../../../core/shared/bitstream.model';
13+
import { getPaginatedListPayload } from '../../../../../../../core/shared/operators';
14+
import { BitstreamRenderingModelComponent } from '../bitstream-rendering-model';
15+
16+
@Component({
17+
// eslint-disable-next-line @angular-eslint/component-selector
18+
selector: 'div[ds-image]',
19+
templateUrl: './image.component.html',
20+
styleUrls: ['./image.component.scss'],
21+
standalone: true,
22+
imports: [
23+
AsyncPipe,
24+
],
25+
})
26+
export class ImageComponent extends BitstreamRenderingModelComponent implements OnInit {
27+
28+
bitstream = new BehaviorSubject<Bitstream>(null);
29+
30+
imageUrl$: Observable<string>;
31+
32+
ngOnInit(): void {
33+
this.getBitstreamsByItem().pipe(
34+
getPaginatedListPayload(),
35+
map((filteredBitstreams: Bitstream[]) => filteredBitstreams.length > 0 ? filteredBitstreams[0] : null),
36+
).subscribe((image) => {
37+
this.bitstream.next(image);
38+
});
39+
40+
this.imageUrl$ = this.bitstream.asObservable().pipe(
41+
map((bitstream) => bitstream?._links?.content?.href),
42+
);
43+
44+
}
45+
46+
backgroundImageUrl(url: string) {
47+
return `url('${url}')`;
48+
}
49+
50+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<div class="mb-2">
2+
<div class="{{field.styleValue}}">
3+
<ds-truncatable [id]="truncatableId">
4+
<ds-truncatable-part [id]="truncatableId" [minLines]="8">
5+
<ds-markdown-viewer [value]="metadataValue.value"></ds-markdown-viewer>
6+
</ds-truncatable-part>
7+
</ds-truncatable>
8+
</div>
9+
</div>

src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/longmarkdown/longmarkdown.component.scss

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
import {
2+
ComponentFixture,
3+
TestBed,
4+
waitForAsync,
5+
} from '@angular/core/testing';
6+
import { By } from '@angular/platform-browser';
7+
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
8+
import {
9+
TranslateLoader,
10+
TranslateModule,
11+
} from '@ngx-translate/core';
12+
import { MockComponent } from 'ng-mocks';
13+
import { MarkdownViewerComponent } from 'src/app/shared/markdown-viewer/markdown-viewer.component';
14+
import { TruncatableComponent } from 'src/app/shared/truncatable/truncatable.component';
15+
import { TruncatablePartComponent } from 'src/app/shared/truncatable/truncatable-part/truncatable-part.component';
16+
17+
import { LayoutField } from '../../../../../../../core/layout/models/box.model';
18+
import { Item } from '../../../../../../../core/shared/item.model';
19+
import { MetadataValue } from '../../../../../../../core/shared/metadata.models';
20+
import { TranslateLoaderMock } from '../../../../../../../shared/mocks/translate-loader.mock';
21+
import { LongmarkdownComponent } from './longmarkdown.component';
22+
23+
describe('LongmarkdownComponent', () => {
24+
let component: LongmarkdownComponent;
25+
let fixture: ComponentFixture<LongmarkdownComponent>;
26+
27+
const metadataValue = Object.assign(new MetadataValue(), {
28+
'value': 'Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry\'s standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.',
29+
'language': null,
30+
'authority': null,
31+
'confidence': -1,
32+
'place': 0,
33+
});
34+
35+
const testItem = Object.assign(new Item(),
36+
{
37+
type: 'item',
38+
metadata: {
39+
'dc.abstract': [metadataValue],
40+
},
41+
uuid: 'test-item-uuid',
42+
},
43+
);
44+
45+
46+
const mockField: LayoutField = {
47+
'metadata': 'dc.abstract',
48+
'label': 'Preferred name',
49+
'rendering': 'markdown',
50+
'fieldType': 'METADATA',
51+
'style': null,
52+
'styleLabel': 'test-style-label',
53+
'styleValue': 'test-style-value',
54+
'labelAsHeading': false,
55+
'valuesInline': true,
56+
};
57+
58+
beforeEach(waitForAsync(() => {
59+
TestBed.configureTestingModule({
60+
imports: [
61+
TranslateModule.forRoot({
62+
loader: {
63+
provide: TranslateLoader,
64+
useClass: TranslateLoaderMock,
65+
},
66+
}),
67+
BrowserAnimationsModule,
68+
MockComponent(MarkdownViewerComponent),
69+
MockComponent(TruncatableComponent),
70+
MockComponent(TruncatablePartComponent),
71+
],
72+
providers: [
73+
{ provide: 'fieldProvider', useValue: mockField },
74+
{ provide: 'itemProvider', useValue: testItem },
75+
{ provide: 'metadataValueProvider', useValue: metadataValue },
76+
{ provide: 'renderingSubTypeProvider', useValue: '' },
77+
{ provide: 'tabNameProvider', useValue: '' },
78+
],
79+
declarations: [LongmarkdownComponent],
80+
})
81+
.compileComponents();
82+
}));
83+
84+
beforeEach(() => {
85+
fixture = TestBed.createComponent(LongmarkdownComponent);
86+
component = fixture.componentInstance;
87+
fixture.detectChanges();
88+
});
89+
90+
it('should create', () => {
91+
expect(component).toBeTruthy();
92+
});
93+
94+
it('check metadata rendering', (done) => {
95+
const spanValueFound = fixture.debugElement.queryAll(By.css('ds-markdown-viewer'));
96+
expect(spanValueFound.length).toBe(1);
97+
done();
98+
});
99+
100+
it('check value style', (done) => {
101+
const spanValueFound = fixture.debugElement.queryAll(By.css('.test-style-value'));
102+
expect(spanValueFound.length).toBe(1);
103+
done();
104+
});
105+
106+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import {
2+
Component,
3+
OnInit,
4+
} from '@angular/core';
5+
import { MarkdownViewerComponent } from 'src/app/shared/markdown-viewer/markdown-viewer.component';
6+
import { TruncatableComponent } from 'src/app/shared/truncatable/truncatable.component';
7+
import { TruncatablePartComponent } from 'src/app/shared/truncatable/truncatable-part/truncatable-part.component';
8+
9+
import { RenderingTypeValueModelComponent } from '../rendering-type-value.model';
10+
11+
/**
12+
* This component renders the markdown metadata fields with a show more button
13+
*/
14+
@Component({
15+
// eslint-disable-next-line @angular-eslint/component-selector
16+
selector: 'div[ds-longmarkdown]',
17+
templateUrl: './longmarkdown.component.html',
18+
styleUrls: ['./longmarkdown.component.scss'],
19+
standalone: true,
20+
imports: [
21+
MarkdownViewerComponent,
22+
TruncatableComponent,
23+
TruncatablePartComponent,
24+
],
25+
})
26+
export class LongmarkdownComponent extends RenderingTypeValueModelComponent implements OnInit {
27+
28+
/**
29+
* Id for truncable component
30+
*/
31+
truncatableId: string;
32+
33+
ngOnInit(): void {
34+
this.truncatableId = `${this.item.id}_${this.field.metadata}_md`;
35+
}
36+
}

0 commit comments

Comments
 (0)