Skip to content

Commit c8c11ea

Browse files
committed
Merge branch 'dspace-cris-2023_02_x' into task/ux-plus-cris-2023_02_x/UXP-184
# Conflicts: # src/app/shared/form/vocabulary-treeview/vocabulary-treeview.component.html # src/app/shared/form/vocabulary-treeview/vocabulary-treeview.component.spec.ts # src/app/shared/form/vocabulary-treeview/vocabulary-treeview.component.ts # src/app/shared/shared.module.ts
2 parents 209811c + 2efde9c commit c8c11ea

11 files changed

Lines changed: 163 additions & 47 deletions

src/app/bulk-import/bulk-import-page.component.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ <h2 class="mb-3">{{ 'bulk-import.title' | translate }}</h2>
2222
{{'bulk-import.submit' | translate}}
2323
</span>
2424
</button>
25-
<button class="btn btn-outline-secondary float-right mr-2" (click)="goBack()">
25+
<button type="button" class="btn btn-outline-secondary float-right mr-2" (click)="goBack()">
2626
{{'bulk-import.back' | translate}}
2727
</button>
2828

src/app/shared/date.util.spec.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,11 @@
1-
import { dateToString, dateToNgbDateStruct, dateToISOFormat, isValidDate, yearFromString } from './date.util';
1+
import {
2+
dateToString,
3+
dateToNgbDateStruct,
4+
dateToISOFormat,
5+
isValidDate,
6+
yearFromString,
7+
localeDate
8+
} from './date.util';
29

310
describe('Date Utils', () => {
411

@@ -104,4 +111,15 @@ describe('Date Utils', () => {
104111
expect(yearFromString('test')).toBeNull();
105112
});
106113
});
114+
115+
describe('localeDate', () => {
116+
it('should return the date in the current locale', () => {
117+
expect(localeDate('2022-06-03', 'en')).toEqual('June 3, 2022');
118+
expect(localeDate('2022-06-03', 'it')).toEqual('3 giugno 2022');
119+
expect(localeDate('2022-06', 'en')).toEqual('June 2022');
120+
expect(localeDate('2022-06', 'it')).toEqual('giugno 2022');
121+
expect(localeDate('2022', 'en')).toEqual('2022');
122+
expect(localeDate('2022', 'it')).toEqual('2022');
123+
});
124+
});
107125
});

src/app/shared/date.util.ts

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -108,16 +108,19 @@ export function yearFromString(date: string) {
108108
}
109109

110110

111-
export function localeDate(date: string, locale?: string) {
111+
export function localeDate(date: string, locale?: string): string {
112+
const parts = date.split('-').map(part => parseInt(part, 10));
113+
const year = parts[0];
114+
const month = parts.length > 1 ? parts[1] - 1 : 0; // Default to January if no month
115+
const day = parts.length > 2 ? parts[2] : 1; // Default to the first day if no day
112116

113-
const tokens = date.split('-').length;
117+
const dateObj = new Date(year, month, day);
114118

115-
const options: Intl.DateTimeFormatOptions = {
119+
const options: Intl.DateTimeFormatOptions = {
116120
year: 'numeric',
117-
month: tokens >= 2 ? 'long' : undefined,
118-
day: tokens >= 3 ? 'numeric' : undefined,
121+
month: parts.length > 1 ? 'long' : undefined, // Show month only if provided
122+
day: parts.length > 2 ? 'numeric' : undefined, // Show day only if provided
119123
};
120124

121-
return new Date(date).toLocaleDateString(locale, options);
122-
125+
return dateObj.toLocaleDateString(locale, options);
123126
}

src/app/shared/entity-dropdown/entity-dropdown.component.html

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,12 @@
1313
{{'submission.sections.general.no-entity' | translate}}
1414
</button>
1515
</li>
16-
<li *ngFor="let listItem of searchListEntity" class="entity-item text-primary">
16+
<li *ngFor="let listItem of searchListEntity | dsSort: 'translatedLabel'" class="entity-item text-primary">
1717
<button class="dropdown-item"
1818
role="menuitem"
19-
title="{{ listItem.label }}"
19+
title="{{ listItem.translatedLabel }}"
2020
(click)="onSelect(listItem)">
21-
<span class="text-truncate font-weight-bold">{{ listItem.label.toLowerCase() + '.listelement.badge' | translate }}</span>
21+
<span class="text-truncate font-weight-bold">{{ listItem.translatedLabel }}</span>
2222
</button>
2323
</li>
2424
<li *ngIf="(isLoadingList | async)">

src/app/shared/entity-dropdown/entity-dropdown.component.spec.ts

Lines changed: 10 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,22 +3,16 @@ import { EntityDropdownComponent } from './entity-dropdown.component';
33
import { getTestScheduler } from 'jasmine-marbles';
44
import { createSuccessfulRemoteDataObject$ } from '../remote-data.utils';
55
import { ItemType } from '../../core/shared/item-relationships/item-type.model';
6-
import { ChangeDetectorRef, NO_ERRORS_SCHEMA, Pipe, PipeTransform } from '@angular/core';
6+
import { ChangeDetectorRef, NO_ERRORS_SCHEMA } from '@angular/core';
77
import { EntityTypeDataService } from '../../core/data/entity-type-data.service';
88
import { TestScheduler } from 'rxjs/testing';
99
import { By } from '@angular/platform-browser';
1010
import { createPaginatedList } from '../testing/utils.test';
1111
import { ItemExportFormatService } from '../../core/itemexportformat/item-export-format.service';
1212
import { of } from 'rxjs/internal/observable/of';
1313
import { ItemExportFormat, ItemExportFormatMap } from '../../core/itemexportformat/model/item-export-format.model';
14-
15-
// eslint-disable-next-line @angular-eslint/pipe-prefix
16-
@Pipe({ name: 'translate' })
17-
class MockTranslatePipe implements PipeTransform {
18-
transform(value: string): string {
19-
return value;
20-
}
21-
}
14+
import { TranslateService } from '@ngx-translate/core';
15+
import { SortPipe } from '../utils/sort.pipe';
2216

2317
const entities: ItemType[] = [
2418
Object.assign(new ItemType(), {
@@ -91,18 +85,23 @@ describe('EntityDropdownComponent', () => {
9185
byEntityTypeAndMolteplicity: jasmine.createSpy('byEntityTypeAndMolteplicity')
9286
});
9387

94-
let translatePipeSpy: jasmine.Spy;
88+
const translateServiceMock: any = {
89+
instant(name) {
90+
return 'Statistics';
91+
}
92+
};
9593

9694
const paginatedEntities = createPaginatedList(entities);
9795
const paginatedEntitiesRD$ = createSuccessfulRemoteDataObject$(paginatedEntities);
9896

9997
beforeEach(waitForAsync(() => {
10098
TestBed.configureTestingModule({
10199
imports: [],
102-
declarations: [EntityDropdownComponent, MockTranslatePipe],
100+
declarations: [EntityDropdownComponent, SortPipe],
103101
providers: [
104102
{ provide: EntityTypeDataService, useValue: entityTypeServiceMock },
105103
{ provide: ItemExportFormatService, useValue: itemExportFormatServiceMock },
104+
{ provide: TranslateService, useValue: translateServiceMock },
106105
ChangeDetectorRef
107106
],
108107
schemas: [NO_ERRORS_SCHEMA]
@@ -119,15 +118,6 @@ describe('EntityDropdownComponent', () => {
119118
componentAsAny.entityTypeService.getAllAuthorizedRelationshipTypeImport.and.returnValue(paginatedEntitiesRD$);
120119
componentAsAny.itemExportFormatService.byEntityTypeAndMolteplicity.and.returnValue(of(entityFormatList));
121120
component.isSubmission = true;
122-
123-
translatePipeSpy = spyOn(MockTranslatePipe.prototype, 'transform');
124-
});
125-
126-
it('should translate entries', () => {
127-
scheduler.schedule(() => fixture.detectChanges());
128-
scheduler.flush();
129-
130-
expect(translatePipeSpy).toHaveBeenCalledWith('entity_1.listelement.badge');
131121
});
132122

133123
it('should init component with entities list', () => {

src/app/shared/entity-dropdown/entity-dropdown.component.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import {
2323
} from '../../core/itemexportformat/item-export-format.service';
2424
import { createSuccessfulRemoteDataObject } from '../remote-data.utils';
2525
import { FindListOptions } from '../../core/data/find-list-options.model';
26+
import { TranslateService } from '@ngx-translate/core';
2627

2728
@Component({
2829
selector: 'ds-entity-dropdown',
@@ -91,12 +92,14 @@ export class EntityDropdownComponent implements OnInit, OnDestroy {
9192
* @param {EntityTypeDataService} entityTypeService
9293
* @param {ItemExportFormatService} itemExportFormatService
9394
* @param {ElementRef} el
95+
* @param {TranslateService} translate
9496
*/
9597
constructor(
9698
private changeDetectorRef: ChangeDetectorRef,
9799
private entityTypeService: EntityTypeDataService,
98100
private itemExportFormatService: ItemExportFormatService,
99-
private el: ElementRef
101+
private el: ElementRef,
102+
private translate: TranslateService
100103
) { }
101104

102105
/**
@@ -194,12 +197,21 @@ export class EntityDropdownComponent implements OnInit, OnDestroy {
194197
}
195198
this.searchListEntity$ = searchListEntity$.pipe(
196199
switchMap((entityType: RemoteData<PaginatedList<ItemType>>) => entityType.payload.page),
200+
map((item: ItemType) => {
201+
return {
202+
...item,
203+
translatedLabel: this.translate.instant(`${item.label?.toLowerCase()}.listelement.badge`)
204+
};
205+
}
206+
),
197207
reduce((acc: any, value: any) => [...acc, value], []),
198208
startWith([])
199209
);
200210
this.subs.push(
201211
this.searchListEntity$.subscribe({
202-
next: (result: ItemType[]) => { this.searchListEntity.push(...result); },
212+
next: (result: ItemType[]) => {
213+
this.searchListEntity = [...this.searchListEntity, ...result];
214+
},
203215
complete: () => { this.hideShowLoader(false); this.changeDetectorRef.detectChanges(); }
204216
})
205217
);

src/app/shared/form/vocabulary-treeview/vocabulary-treeview.component.html

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,20 @@
11
<ds-alert *ngIf="description" [content]="description" [type]="'alert-info'"></ds-alert>
2-
<div *ngIf="enabledSearch" class="treeview-header row mb-1" data-test="search-field">
2+
<div *ngIf="enabledSearch" class="treeview-header row" data-test="search-field">
33
<div class="col-12 d-flex">
44
<div class="input-group">
5-
<input type="text" class="form-control" [(ngModel)]="searchText" (keyup.enter)="search()"
6-
[placeholder]="'vocabulary-treeview.search.form.search-placeholder' | translate">
5+
<input type="text" class="form-control" [(ngModel)]="searchText" (keyup.enter)="search()">
76
<div class="input-group-append" id="button-addon4">
8-
<button class="btn btn-outline-primary" type="button" (click)="search()" [disabled]="!isSearchEnabled()">
9-
{{'vocabulary-treeview.search.form.search' | translate}}
10-
</button>
11-
<button class="btn btn-outline-secondary" type="button" (click)="reset()">
12-
{{'vocabulary-treeview.search.form.reset' | translate}}
13-
</button>
14-
<button class="btn btn-outline-primary" type="button" (click)="add()" [disabled]="this.vocabularyOptions.closed">
15-
{{'vocabulary-treeview.search.form.add' | translate}}
7+
<button class="btn btn-primary" type="button" (click)="search()" [disabled]="!isSearchEnabled()">
8+
<span><i class="fas fa-search"></i> {{'vocabulary-treeview.search.form.search' | translate}}</span>
169
</button>
1710
</div>
1811
</div>
12+
<button class="btn btn-secondary ml-3 flex-shrink-0" type="button" (click)="reset()">
13+
<span><i class="fas fa-eraser"></i> {{'vocabulary-treeview.search.form.reset' | translate}}</span>
14+
</button>
15+
<button *ngIf="showAdd && !this.vocabularyOptions.closed" class="btn btn-secondary ml-3 flex-shrink-0" type="button" (click)="add()">
16+
<span><i class="fas fa-plus"></i> {{'vocabulary-treeview.search.form.add' | translate}}</span>
17+
</button>
1918
</div>
2019
</div>
2120
<div class="treeview-container">
@@ -66,7 +65,7 @@ <h2 *ngIf="!(loading | async) && dataSource.data.length === 0" class="h4 text-ce
6665
<button type="button" class="btn btn-default px-2 mr-1" cdkTreeNodeToggle
6766
[attr.aria-label]="'toggle ' + node.name"
6867
(click)="loadChildren(node)">
69-
<i class="fas fa-fw {{treeControl.isExpanded(node) ? 'fa-angle-down' : 'fa-angle-right'}}"></i>
68+
<span class="fas fa-fw {{treeControl.isExpanded(node) ? 'fa-angle-down' : 'fa-angle-right'}}"></span>
7069
</button>
7170

7271
<label *ngIf="multiSelect" class="d-flex align-items-center m-0 p-0 form-check"

src/app/shared/form/vocabulary-treeview/vocabulary-treeview.component.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import { VocabularyTreeFlattener } from './vocabulary-tree-flattener';
1515
import { VocabularyTreeFlatDataSource } from './vocabulary-tree-flat-data-source';
1616
import { VocabularyService } from '../../../core/submission/vocabularies/vocabulary.service';
1717
import { FormFieldMetadataValueObject } from '../builder/models/form-field-metadata-value.model';
18+
import { AlertType } from '../../alert/alert-type';
1819
import { Metadata } from '../../../core/shared/metadata.utils';
1920

2021
export type VocabularyTreeItemType = FormFieldMetadataValueObject | VocabularyEntry | VocabularyEntryDetail;

src/app/shared/shared.module.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,7 @@ import { MetadataLinkViewAvatarPopoverComponent } from './metadata-link-view/met
359359
import { MetadataLinkViewOrcidComponent } from './metadata-link-view/metadata-link-view-orcid/metadata-link-view-orcid.component';
360360
import { SwitchComponent } from './switch/switch.component';
361361
import { StickyPopoverDirective } from './metadata-link-view/sticky-popover.directive';
362+
import { SortPipe } from './utils/sort.pipe';
362363
import { CardsBrowseElementsComponent } from './browse-most-elements/cards-browse-elements/cards-browse-elements.component';
363364
import {
364365
ThemedCardsBrowseElementsComponent
@@ -409,7 +410,8 @@ const PIPES = [
409410
ConsolePipe,
410411
ObjNgFor,
411412
BrowserOnlyPipe,
412-
ShortNumberPipe
413+
ShortNumberPipe,
414+
SortPipe,
413415
];
414416

415417
const COMPONENTS = [
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import { SortPipe } from './sort.pipe';
2+
3+
describe('SortPipe', () => {
4+
let pipe: SortPipe;
5+
6+
beforeEach(() => {
7+
pipe = new SortPipe();
8+
});
9+
10+
it('should return the original value if value is null or undefined', () => {
11+
expect(pipe.transform(null)).toBeNull();
12+
expect(pipe.transform(undefined)).toBeUndefined();
13+
});
14+
15+
it('should return the original value if no order is invalid', () => {
16+
const inputArray = [3, 1, 2];
17+
expect(pipe.transform(inputArray, '', null)).toEqual(inputArray);
18+
});
19+
20+
it('should sort the array in ascending order if no column is provided', () => {
21+
const inputArray = [3, 1, 2];
22+
const sortedArray = [1, 2, 3];
23+
expect(pipe.transform(inputArray, '')).toEqual(sortedArray);
24+
});
25+
26+
it('should sort the array in descending order if no column is provided and order is desc', () => {
27+
const inputArray = [3, 1, 2];
28+
const sortedArray = [3, 2, 1];
29+
expect(pipe.transform(inputArray, '', 'desc')).toEqual(sortedArray);
30+
});
31+
32+
it('should return the original array if it contains one or fewer elements', () => {
33+
const inputArray = [1];
34+
expect(pipe.transform(inputArray)).toEqual(inputArray);
35+
expect(pipe.transform([])).toEqual([]);
36+
});
37+
38+
it('should sort the array by a specific column in ascending order', () => {
39+
const inputArray = [
40+
{ label: 'banana' },
41+
{ label: 'apple' },
42+
{ label: 'cherry' }
43+
];
44+
const sortedArray = [
45+
{ label: 'apple' },
46+
{ label: 'banana' },
47+
{ label: 'cherry' }
48+
];
49+
expect(pipe.transform(inputArray, 'label')).toEqual(sortedArray);
50+
});
51+
52+
it('should sort the array by a specific column in descending order', () => {
53+
const inputArray = [
54+
{ label: 'banana' },
55+
{ label: 'apple' },
56+
{ label: 'cherry' }
57+
];
58+
const sortedArray = [
59+
{ label: 'cherry' },
60+
{ label: 'banana' },
61+
{ label: 'apple' }
62+
];
63+
expect(pipe.transform(inputArray, 'label', 'desc')).toEqual(sortedArray);
64+
});
65+
});

0 commit comments

Comments
 (0)