Skip to content

Commit 7cf4661

Browse files
Sufiyan Shaikhatarix83
Sufiyan Shaikh
authored andcommitted
Merged in DSC-821 (pull request DSpace#382)
[DSC-821] Improve crisref icon implementation Approved-by: Giuseppe Digilio
2 parents e99cb45 + e966379 commit 7cf4661

File tree

17 files changed

+544
-155
lines changed

17 files changed

+544
-155
lines changed

config/config.example.yml

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -291,12 +291,37 @@ crisLayout:
291291
- name: mailto
292292
baseUrl: "mailto:"
293293
crisRef:
294+
- entityType: PRIVATE
295+
entityStyle:
296+
"dc.contributor.author":
297+
icon: fa fa-user
298+
style: text-muted
294299
- entityType: DEFAULT
295-
icon: "fa fa-info"
300+
entityStyle:
301+
default:
302+
icon: fa fa-user
303+
style: text-info
296304
- entityType: PERSON
297-
icon: "fa fa-user"
305+
entityStyle:
306+
person:
307+
icon: fa fa-user
308+
style: text-success
309+
personStaff:
310+
icon: fa fa-user
311+
style: text-danger
312+
default:
313+
icon: fa fa-user
314+
style: text-info
298315
- entityType: ORGUNIT
299-
icon: "fa fa-university"
316+
entityStyle:
317+
default:
318+
icon: fa fa-university
319+
style: text-success
320+
- entityType: PROJECT
321+
entityStyle:
322+
default:
323+
icon: fas fa-project-diagram
324+
style: text-success
300325
itemPage:
301326
OrgUnit:
302327
orientation: vertical
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<div [class]="field.styleValue">
22
<span class="d-inline-flex align-items-center text-value">
3-
<ds-metadata-link-view [item]="item" [metadata]="metadataValue"></ds-metadata-link-view>
3+
<ds-metadata-link-view [item]="item" [metadata]="metadataValue" [metadataName]="field.metadata"></ds-metadata-link-view>
44
</span>
5-
</div>
5+
</div>
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
import { ComponentFixture, TestBed } from '@angular/core/testing';
2+
import { Component } from '@angular/core';
3+
import { By } from '@angular/platform-browser';
4+
5+
import { EntityIconDirective } from './entity-icon.directive';
6+
7+
describe('EntityIconDirective', () => {
8+
let component: TestComponent;
9+
let fixture: ComponentFixture<TestComponent>;
10+
11+
beforeEach(async () => {
12+
await TestBed.configureTestingModule({
13+
declarations: [
14+
EntityIconDirective,
15+
TestComponent
16+
]
17+
})
18+
.compileComponents();
19+
});
20+
21+
describe('with default value provided', () => {
22+
beforeEach(() => {
23+
fixture = TestBed.createComponent(TestComponent);
24+
component = fixture.componentInstance;
25+
fixture.detectChanges();
26+
});
27+
28+
it('should create', () => {
29+
expect(component).toBeTruthy();
30+
});
31+
32+
it('should display a text-success icon', () => {
33+
const successIcon = fixture.debugElement.query(By.css('[data-test="entityTestComponent"]')).query(By.css('i.text-success'));
34+
expect(successIcon).toBeTruthy();
35+
});
36+
37+
it('should display a text-success icon after span', () => {
38+
const successIcon = fixture.debugElement.query(By.css('[data-test="entityTestComponent"]')).query(By.css('i.text-success'));
39+
const entityElement = fixture.debugElement.query(By.css('[data-test="entityTestComponent"]'));
40+
// position 1 because the icon is after the span
41+
expect(entityElement.nativeElement.children[1]).toBe(successIcon.nativeNode);
42+
});
43+
});
44+
45+
describe('with primary value provided', () => {
46+
beforeEach(() => {
47+
fixture = TestBed.createComponent(TestComponent);
48+
component = fixture.componentInstance;
49+
component.metadata.entityType = 'person';
50+
component.metadata.entityStyle = 'personStaff';
51+
component.iconPosition = 'before';
52+
fixture.detectChanges();
53+
});
54+
55+
it('should display a text-primary icon', () => {
56+
const primaryIcon = fixture.debugElement.query(By.css('[data-test="entityTestComponent"]')).query(By.css('i.text-primary'));
57+
expect(primaryIcon).toBeTruthy();
58+
});
59+
60+
it('should display a text-primary icon before span', () => {
61+
const primaryIcon = fixture.debugElement.query(By.css('[data-test="entityTestComponent"]')).query(By.css('i.text-primary'));
62+
const entityElement = fixture.debugElement.query(By.css('[data-test="entityTestComponent"]'));
63+
// position 0 because the icon is before the span
64+
expect(entityElement.nativeElement.children[0]).toBe(primaryIcon.nativeNode);
65+
});
66+
});
67+
68+
describe('when given type doesn\'t exist and fallback on default disabled', () => {
69+
beforeEach(() => {
70+
fixture = TestBed.createComponent(TestComponent);
71+
component = fixture.componentInstance;
72+
component.fallbackOnDefault = false
73+
component.metadata.entityType = 'TESTFAKE';
74+
component.metadata.entityStyle = 'personFallback';
75+
component.iconPosition = 'before';
76+
fixture.detectChanges();
77+
});
78+
79+
it('should not display a text-primary icon', () => {
80+
const primaryIcon = fixture.debugElement.query(By.css('[data-test="entityTestComponent"]')).query(By.css('i'));
81+
expect(primaryIcon).toBeFalsy();
82+
});
83+
});
84+
85+
describe('when given style doesn\'t exist and fallback on default disabled', () => {
86+
beforeEach(() => {
87+
fixture = TestBed.createComponent(TestComponent);
88+
component = fixture.componentInstance;
89+
component.fallbackOnDefault = false
90+
component.metadata.entityType = 'person';
91+
component.metadata.entityStyle = 'personFallback';
92+
component.iconPosition = 'before';
93+
fixture.detectChanges();
94+
});
95+
96+
it('should not display a text-primary icon', () => {
97+
const primaryIcon = fixture.debugElement.query(By.css('[data-test="entityTestComponent"]')).query(By.css('i'));
98+
expect(primaryIcon).toBeFalsy();
99+
});
100+
});
101+
102+
});
103+
104+
// declare a test component
105+
@Component({
106+
selector: 'ds-test-cmp',
107+
template: `<div [attr.data-test]="'entityTestComponent'">
108+
<span dsEntityIcon
109+
[iconPosition]="iconPosition"
110+
[entityType]="metadata.entityType"
111+
[entityStyle]="metadata.entityStyle"
112+
[fallbackOnDefault]="fallbackOnDefault">{{metadata.value}}</span></div>`
113+
})
114+
class TestComponent {
115+
116+
metadata = {
117+
authority: null,
118+
value: 'Test',
119+
orcidAuthenticated: null,
120+
entityType: 'default',
121+
entityStyle: 'default'
122+
};
123+
iconPosition = 'after';
124+
fallbackOnDefault = true;
125+
126+
}
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
import { Directive, ElementRef, Input, OnInit } from '@angular/core';
2+
3+
import { environment } from '../../../environments/environment';
4+
import { CrisRefConfig, CrisRefEntityStyleConfig } from 'src/config/layout-config.interfaces';
5+
import { isEmpty, isNotEmpty } from '../empty.util';
6+
7+
/**
8+
* Directive to add to the element a entity icon based on metadata entity type and entity style
9+
*/
10+
@Directive({
11+
selector: '[dsEntityIcon]'
12+
})
13+
export class EntityIconDirective implements OnInit {
14+
15+
/**
16+
* The metadata entity type
17+
*/
18+
@Input() entityType = 'default';
19+
20+
/**
21+
* The metadata entity style
22+
*/
23+
@Input() entityStyle: string|string[] = 'default';
24+
25+
/**
26+
* A boolean representing if to fallback on default style if the given one is not found
27+
*/
28+
@Input() fallbackOnDefault = true;
29+
30+
/**
31+
* A boolean representing if to show html icon before or after
32+
*/
33+
@Input() iconPosition = 'after';
34+
35+
/**
36+
* A configuration representing crisRef values
37+
*/
38+
confValue = environment.crisLayout.crisRef;
39+
40+
/**
41+
* Initialize instance variables
42+
*
43+
* @param {ElementRef} elem
44+
*/
45+
constructor(private elem: ElementRef) {
46+
}
47+
48+
/**
49+
* Adding icon to element oninit
50+
*/
51+
ngOnInit() {
52+
const crisRefConfig: CrisRefConfig = this.getCrisRefConfigByType(this.entityType);
53+
if (isNotEmpty(crisRefConfig)) {
54+
const crisStyle: CrisRefEntityStyleConfig = this.getCrisRefEntityStyleConfig(crisRefConfig, this.entityStyle);
55+
if (isNotEmpty(crisStyle)) {
56+
this.addIcon(crisStyle);
57+
}
58+
}
59+
}
60+
61+
/**
62+
* Return the CrisRefConfig by the given type
63+
*
64+
* @param type
65+
* @private
66+
*/
67+
private getCrisRefConfigByType(type: string): CrisRefConfig {
68+
let filteredConf: CrisRefConfig = this.confValue.find((config) => config.entityType.toUpperCase() === type.toUpperCase());
69+
if (isEmpty(filteredConf) && this.fallbackOnDefault) {
70+
filteredConf = this.confValue.find((config) => config.entityType.toUpperCase() === 'DEFAULT');
71+
}
72+
73+
return filteredConf;
74+
}
75+
76+
/**
77+
* Return the CrisRefEntityStyleConfig by the given style
78+
*
79+
* @param crisConfig
80+
* @param styles
81+
* @private
82+
*/
83+
private getCrisRefEntityStyleConfig(crisConfig: CrisRefConfig, styles: string|string[]): CrisRefEntityStyleConfig {
84+
let filteredConf: CrisRefEntityStyleConfig;
85+
if (Array.isArray(styles)) {
86+
styles.forEach((style) => {
87+
if (Object.keys(crisConfig.entityStyle).includes(style)) {
88+
filteredConf = crisConfig.entityStyle[style]
89+
}
90+
})
91+
} else {
92+
filteredConf = crisConfig.entityStyle[styles];
93+
}
94+
95+
if (isEmpty(filteredConf) && this.fallbackOnDefault) {
96+
filteredConf = crisConfig.entityStyle.default;
97+
}
98+
99+
return filteredConf;
100+
}
101+
102+
/**
103+
* Attach icon to HTML element
104+
*
105+
* @param crisStyle
106+
* @private
107+
*/
108+
private addIcon(crisStyle: CrisRefEntityStyleConfig): void {
109+
const iconElement = `<i class="${crisStyle.icon} ${crisStyle.style}"></i>`;
110+
if (this.iconPosition === 'after') {
111+
this.elem.nativeElement.insertAdjacentHTML('afterend', '&nbsp;' + iconElement);
112+
} else {
113+
this.elem.nativeElement.insertAdjacentHTML('beforebegin', iconElement + '&nbsp;');
114+
}
115+
}
116+
117+
}
Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,34 @@
1-
<div class="d-inline-block" *ngVar="(metadata$ | async) as metadata">
2-
<ng-container *ngIf="metadata" [ngTemplateOutlet]="metadata?.authority ? withAuthority : withoutAuthority"
3-
[ngTemplateOutletContext]="{metadata:metadata}"></ng-container>
1+
<div class="d-inline-block" *ngVar="(metadataView$ | async) as metadataView">
2+
<ng-container *ngIf="metadataView" [ngTemplateOutlet]="metadataView?.authority ? linkToAuthority : (metadataView?.entityType ? textWithIcon : textWithoutIcon)"
3+
[ngTemplateOutletContext]="{metadataView: metadataView}"></ng-container>
44
</div>
55

6-
<ng-template style="display:flex;" #withAuthority let-metadata="metadata">
7-
<a rel="noopener noreferrer"
8-
[routerLink]="['/items/' + metadata.authority]">
9-
<span>{{metadata.value}}</span>&nbsp;<i class="{{metadata.icon}}"></i>
6+
<ng-template class="d-flex" #linkToAuthority let-metadataView="metadataView">
7+
<a rel="noopener noreferrer" data-test="linkToAuthority"
8+
[routerLink]="['/items/' + metadataView.authority]">
9+
<span dsEntityIcon
10+
[iconPosition]="iconPosition"
11+
[entityType]="metadataView.entityType"
12+
[entityStyle]="metadataView.entityStyle">{{metadataView.value}}</span>
1013
</a>
11-
<img *ngIf="metadata.orcidAuthenticated"
14+
<img *ngIf="metadataView.orcidAuthenticated"
1215
placement="top"
13-
ngbTooltip="{{ metadata.orcidAuthenticated }}"
16+
ngbTooltip="{{ metadataView.orcidAuthenticated }}"
1417
class="orcid-icon"
1518
alt="orcid-logo"
16-
src="assets/images/orcid.logo.icon.svg"/>
19+
src="assets/images/orcid.logo.icon.svg"
20+
data-test="orcidIcon"/>
1721
</ng-template>
1822

19-
<ng-template #withoutAuthority let-metadata="metadata">
20-
<span>{{normalizeValue(metadata.value)}}</span>
23+
<ng-template #textWithIcon let-metadataView="metadataView">
24+
<span dsEntityIcon
25+
data-test="textWithIcon"
26+
[iconPosition]="iconPosition"
27+
[entityType]="metadataView.entityType"
28+
[entityStyle]="metadataView.entityStyle"
29+
[fallbackOnDefault]="false">{{normalizeValue(metadataView.value)}}</span>
30+
</ng-template>
31+
32+
<ng-template #textWithoutIcon let-metadataView="metadataView">
33+
<span data-test="textWithoutIcon">{{normalizeValue(metadataView.value)}}</span>
2134
</ng-template>

0 commit comments

Comments
 (0)