Skip to content

Commit 926d0df

Browse files
[DSC-2544] fix link.label and improve valuepair
1 parent c9f59e5 commit 926d0df

5 files changed

Lines changed: 165 additions & 10 deletions

File tree

src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/link/link.component.spec.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,4 +239,42 @@ describe('LinkComponent', () => {
239239
});
240240
});
241241
});
242+
243+
describe('parseLabelValue', () => {
244+
245+
beforeEach(() => {
246+
fixture.detectChanges();
247+
});
248+
249+
it('should correctly extract label and URL from [Label](URL) format', () => {
250+
const input = '[My Label](https://example.com/path)';
251+
const result = component.parseLabelValue(input);
252+
253+
expect(result).toEqual({
254+
label: 'My Label',
255+
value: 'https://example.com/path'
256+
});
257+
});
258+
259+
it('should return the same value if input does not match [Label](URL) format', () => {
260+
const input = 'Just a plain URL';
261+
const result = component.parseLabelValue(input);
262+
263+
expect(result).toEqual({
264+
label: input,
265+
value: input
266+
});
267+
});
268+
269+
it('should handle empty string gracefully', () => {
270+
const input = '';
271+
const result = component.parseLabelValue(input);
272+
273+
expect(result).toEqual({
274+
label: '',
275+
value: ''
276+
});
277+
});
278+
279+
});
242280
});

src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/link/link.component.ts

Lines changed: 39 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -84,21 +84,51 @@ export class LinkComponent extends RenderingTypeValueModelComponent implements O
8484
let linkText: string;
8585
let metadataValue: string;
8686

87-
if (hasValue(this.renderingSubType) && this.renderingSubType.toUpperCase() === TYPES.EMAIL.toString()) {
88-
this.isEmail = true;
89-
metadataValue = 'mailto:' + this.metadataValue.value;
90-
linkText = (hasValue(this.renderingSubType) &&
91-
this.renderingSubType.toUpperCase() === TYPES.EMAIL.toString()) ? this.metadataValue.value : this.translateService.instant(this.field.label);
87+
if (hasValue(this.renderingSubType) && this.renderingSubType.toUpperCase() === TYPES.EMAIL) {
88+
this.isEmail = true;
89+
metadataValue = 'mailto:' + this.metadataValue.value;
90+
linkText = (hasValue(this.renderingSubType) &&
91+
this.renderingSubType.toUpperCase() === TYPES.EMAIL) ? this.metadataValue.value : this.translateService.instant(this.field.label);
92+
} else if ((hasValue(this.renderingSubType) && this.renderingSubType.toUpperCase() === TYPES.LABEL)) {
93+
// Parse value in format [Label](URL)
94+
const parsedValue = this.parseLabelValue(this.metadataValue.value);
95+
96+
metadataValue = this.getLinkWithProtocol(parsedValue.value);
97+
linkText = parsedValue.label;
9298
} else {
93-
const startsWithProtocol = [/^https?:\/\//, /^ftp:\/\//];
94-
metadataValue = startsWithProtocol.some(rx => rx.test(this.metadataValue.value)) ? this.metadataValue.value : 'http://' + this.metadataValue.value;
95-
linkText = (hasValue(this.renderingSubType) &&
96-
this.renderingSubType.toUpperCase() === TYPES.LABEL.toString()) ? this.translateService.instant(this.field.label) : this.metadataValue.value;
99+
// Use same value for link and label, correcting the protocol for link if needed
100+
metadataValue = this.getLinkWithProtocol(this.metadataValue.value);
101+
linkText = this.metadataValue.value;
97102
}
98103

99104
return {
100105
href: metadataValue,
101106
text: linkText,
102107
};
103108
}
109+
110+
/**
111+
* Exctract label and values for TYPES.LABEL
112+
* @param value
113+
*/
114+
parseLabelValue(input: string): { label: string; value: string } {
115+
const match = input.match(/^\[([^\]]+)\]\(([^)]+)\)$/);
116+
117+
if (!match) {
118+
return {
119+
label: input,
120+
value: input
121+
};
122+
}
123+
124+
return {
125+
label: match[1],
126+
value: match[2],
127+
};
128+
}
129+
130+
getLinkWithProtocol(link: string): string {
131+
const startsWithProtocol = [/^https?:\/\//, /^ftp:\/\//];
132+
return startsWithProtocol.some(rx => rx.test(link)) ? link : 'http://' + link;
133+
}
104134
}
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
<div [class]="field.styleValue" [attr.lang]="metadataValue.language">
2+
<ng-container *ngIf="isMetadataLink !== true; else linkTemplate">
23
<span class="text-value">
34
{{ value$ | async }}
45
</span>
6+
</ng-container>
57
</div>
8+
9+
<ng-template #linkTemplate>
10+
<a [href]="metadataValue.value" class="text-value">
11+
{{ value$ | async }}
12+
</a>
13+
</ng-template>

src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/valuepair/valuepair.component.spec.ts

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,4 +194,69 @@ describe('ValuepairComponent', () => {
194194

195195
});
196196

197+
describe('when the metadata value is a link', () => {
198+
199+
const linkValue = 'https://example.com';
200+
const testFieldLink: LayoutField = {
201+
metadata: 'dc.identifier',
202+
label: 'Identifier',
203+
rendering: 'valuepair.' + VOCABULARY_NAME_2,
204+
fieldType: 'METADATA',
205+
metadataGroup: null,
206+
labelAsHeading: false,
207+
valuesInline: false
208+
};
209+
210+
const testItemLink = Object.assign(new Item(), {
211+
allMetadata: () => [{ value: linkValue, authority: null }],
212+
});
213+
214+
beforeEach(waitForAsync(() => {
215+
TestBed.configureTestingModule({
216+
imports: [
217+
TranslateModule.forRoot({
218+
loader: {
219+
provide: TranslateLoader,
220+
useClass: TranslateLoaderMock
221+
}
222+
}),
223+
],
224+
declarations: [ValuepairComponent, DsDatePipe],
225+
providers: [
226+
{ provide: VocabularyService, useValue: vocabularyServiceSpy },
227+
{ provide: AuthService, useValue: authService },
228+
{ provide: 'fieldProvider', useValue: testFieldLink },
229+
{ provide: 'itemProvider', useValue: testItemLink },
230+
{ provide: 'metadataValueProvider', useValue: { value: linkValue, authority: null } },
231+
{ provide: 'renderingSubTypeProvider', useValue: '' }, // leave empty
232+
{ provide: 'tabNameProvider', useValue: '' },
233+
],
234+
}).compileComponents();
235+
}));
236+
237+
beforeEach(() => {
238+
fixture = TestBed.createComponent(ValuepairComponent);
239+
component = fixture.componentInstance;
240+
241+
// Manually emit the value since vocabulary service won't emit
242+
component.value$.next(linkValue);
243+
244+
fixture.detectChanges();
245+
});
246+
247+
it('should detect that the value is a link', () => {
248+
expect(component.isMetadataLink).toBeTrue();
249+
});
250+
251+
it('should render the value as an <a> tag', () => {
252+
const compiled = fixture.nativeElement as HTMLElement;
253+
const linkEl = compiled.querySelector('a.text-value') as HTMLAnchorElement;
254+
255+
expect(linkEl).toBeTruthy();
256+
expect(linkEl.href).toBe(linkValue + '/'); // Browser adds trailing slash
257+
expect(linkEl.textContent.trim()).toBe(linkValue);
258+
});
259+
});
260+
261+
197262
});

src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/valuepair/valuepair.component.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,12 @@ export class ValuepairComponent extends RenderingTypeValueModelComponent impleme
4141
*/
4242
value$: BehaviorSubject<string> = new BehaviorSubject<string>(null);
4343

44+
/**
45+
* Whether the value is a link
46+
*/
47+
48+
isMetadataLink: boolean;
49+
4450
constructor(
4551
@Inject('fieldProvider') public fieldProvider: LayoutField,
4652
@Inject('itemProvider') public itemProvider: Item,
@@ -68,11 +74,19 @@ export class ValuepairComponent extends RenderingTypeValueModelComponent impleme
6874
getFirstCompletedRemoteData(),
6975
getRemoteDataPayload(),
7076
getPaginatedListPayload(),
71-
map((res) => res?.length > 0 ? res[0] : null),
77+
map((res) => {
78+
return res?.length > 0 ? res[0] : null;
79+
}),
7280
map((res) => res?.display ?? this.metadataValue.value),
7381
take(1),
7482
).subscribe(value => this.value$.next(value));
7583

84+
this.isMetadataLink = this.isLink(this.metadataValue.value);
85+
}
86+
87+
isLink(input: string): boolean {
88+
// check only values with protocol, if missing fix value in value-pair list
89+
return input && (input.startsWith('http://') || input.startsWith('https://'));
7690
}
7791

7892
}

0 commit comments

Comments
 (0)