mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-07 10:04:11 +00:00
Improved icons handler in chips.component.ts
This commit is contained in:
@@ -153,7 +153,13 @@
|
||||
"recent-submissions": "Error fetching recent submissions",
|
||||
"item": "Error fetching item",
|
||||
"objects": "Error fetching objects",
|
||||
"search-results": "Error fetching search results"
|
||||
"search-results": "Error fetching search results",
|
||||
"validation": {
|
||||
"pattern": "This input is restricted by the current pattern: {{ pattern }}.",
|
||||
"license": {
|
||||
"notgranted": "You must grant this license to complete your submission. If you are unable to grant this license at this time you may save your work and return later or remove the submission."
|
||||
}
|
||||
}
|
||||
},
|
||||
"form": {
|
||||
"submit": "Submit",
|
||||
@@ -168,11 +174,6 @@
|
||||
"group-collapse": "Collapse",
|
||||
"group-expand": "Expand",
|
||||
"group-collapse-help": "Click here to collapse",
|
||||
"group-expand-help": "Click here to expand and add more element",
|
||||
"error": {
|
||||
"validation": {
|
||||
"pattern": "This input is restricted by the current pattern: {{ pattern }}."
|
||||
}
|
||||
}
|
||||
"group-expand-help": "Click here to expand and add more element"
|
||||
}
|
||||
}
|
||||
|
@@ -2,25 +2,27 @@
|
||||
<ul class="nav nav-pills d-flex flex-column flex-sm-row" [sortablejs]="chips.getChips()" [sortablejsOptions]="options">
|
||||
<ng-container *ngFor="let c of chips.getChips(); let i = index">
|
||||
<ng-template #tipContent>{{tipText}}</ng-template>
|
||||
<li [ngbTooltip]="tipContent"
|
||||
triggers="manual"
|
||||
#t="ngbTooltip"
|
||||
class="nav-item mr-2 mb-1"
|
||||
(click)="t.close()"
|
||||
(dragstart)="onDragStart(t, i)"
|
||||
(dragend)="onDragEnd(i)"
|
||||
(mouseover)="showTooltip(t, i, c.display)"
|
||||
(mouseout)="t.close()">
|
||||
<li class="nav-item mr-2 mb-1"
|
||||
(dragstart)="onDragStart(i)"
|
||||
(dragend)="onDragEnd(i)">
|
||||
<a class="flex-sm-fill text-sm-center nav-link active"
|
||||
href="#"
|
||||
[ngClass]="{'chip-selected disabled': (editable && c.editMode) || dragged == i}"
|
||||
(click)="t.close();chipsSelected($event, i);">
|
||||
(click)="chipsSelected($event, i);">
|
||||
<span>
|
||||
<ng-container *ngIf="c.hasIcons()">
|
||||
<i *ngFor="let icon of c.icons; let i = index; let l = last"
|
||||
<i *ngFor="let icon of c.icons; let l = last"
|
||||
[ngbTooltip]="tipContent"
|
||||
triggers="manual"
|
||||
#t="ngbTooltip"
|
||||
class="fa {{icon.style}}"
|
||||
[class.mr-1]="!l"
|
||||
[class.mr-2]="l"
|
||||
aria-hidden="true"></i>
|
||||
[class.text-muted]="!(icon.hasAuthority)"
|
||||
aria-hidden="true"
|
||||
(dragstart)="tooltip.close();"
|
||||
(mouseover)="showTooltip(t, i, icon.metadata)"
|
||||
(mouseout)="t.close()"></i>
|
||||
</ng-container>
|
||||
<p class="chip-label text-truncate d-table-cell">{{c.display}}</p><i class="fa fa-times ml-2" (click)="removeChips($event, i)"></i>
|
||||
</span>
|
||||
|
@@ -9,16 +9,8 @@ import { ChipsComponent } from './chips.component';
|
||||
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { SortablejsModule } from 'angular-sortablejs';
|
||||
import { By } from '@angular/platform-browser';
|
||||
|
||||
function createTestComponent<T>(html: string, type: { new(...args: any[]): T }): ComponentFixture<T> {
|
||||
TestBed.overrideComponent(type, {
|
||||
set: {template: html}
|
||||
});
|
||||
const fixture = TestBed.createComponent(type);
|
||||
|
||||
fixture.detectChanges();
|
||||
return fixture as ComponentFixture<T>;
|
||||
}
|
||||
import { FormFieldMetadataValueObject } from '../form/builder/models/form-field-metadata-value.model';
|
||||
import { createTestComponent, hasClass } from '../testing/utils';
|
||||
|
||||
describe('ChipsComponent test suite', () => {
|
||||
|
||||
@@ -35,7 +27,7 @@ describe('ChipsComponent test suite', () => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [
|
||||
NgbModule.forRoot(),
|
||||
SortablejsModule.forRoot({ animation: 150 }),
|
||||
SortablejsModule.forRoot({animation: 150}),
|
||||
],
|
||||
declarations: [
|
||||
ChipsComponent,
|
||||
@@ -51,6 +43,7 @@ describe('ChipsComponent test suite', () => {
|
||||
|
||||
}));
|
||||
|
||||
describe('', () => {
|
||||
// synchronous beforeEach
|
||||
beforeEach(() => {
|
||||
html = `
|
||||
@@ -67,7 +60,9 @@ describe('ChipsComponent test suite', () => {
|
||||
it('should create Chips Component', inject([ChipsComponent], (app: ChipsComponent) => {
|
||||
expect(app).toBeDefined();
|
||||
}));
|
||||
});
|
||||
|
||||
describe('when has items as string', () => {
|
||||
beforeEach(() => {
|
||||
chips = new Chips(['a', 'b', 'c']);
|
||||
chipsFixture = TestBed.createComponent(ChipsComponent);
|
||||
@@ -128,7 +123,7 @@ describe('ChipsComponent test suite', () => {
|
||||
|
||||
de.triggerEventHandler('dragstart', null);
|
||||
|
||||
expect(chipsComp.dragged).toBe(0)
|
||||
expect(chipsComp.dragged).toBe(0);
|
||||
}));
|
||||
|
||||
it('should update chips item order when drag and drop end', fakeAsync(() => {
|
||||
@@ -137,17 +132,56 @@ describe('ChipsComponent test suite', () => {
|
||||
|
||||
de.triggerEventHandler('dragend', null);
|
||||
|
||||
expect(chipsComp.dragged).toBe(-1)
|
||||
expect(chipsComp.dragged).toBe(-1);
|
||||
expect(chipsComp.chips.updateOrder).toHaveBeenCalled();
|
||||
}));
|
||||
});
|
||||
|
||||
it('should show item tooltip on mouse over', fakeAsync(() => {
|
||||
describe('when has items as object', () => {
|
||||
beforeEach(() => {
|
||||
const item = {
|
||||
mainField: new FormFieldMetadataValueObject('main test', null, 'test001'),
|
||||
relatedField: new FormFieldMetadataValueObject('related test', null, 'test002'),
|
||||
otherRelatedField: new FormFieldMetadataValueObject('other related test')
|
||||
};
|
||||
|
||||
const iconsConfig = {
|
||||
mainField: 'fa-user',
|
||||
relatedField: 'fa-user-alt',
|
||||
otherRelatedField: 'fa-user-alt'
|
||||
};
|
||||
chips = new Chips([item], 'display', 'mainField', iconsConfig);
|
||||
chipsFixture = TestBed.createComponent(ChipsComponent);
|
||||
chipsComp = chipsFixture.componentInstance; // TruncatableComponent test instance
|
||||
chipsComp.editable = true;
|
||||
chipsComp.chips = chips;
|
||||
chipsFixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should show icon for every field that has a configured icon', () => {
|
||||
const de = chipsFixture.debugElement.query(By.css('li.nav-item'));
|
||||
const icons = de.queryAll(By.css('i.fa'));
|
||||
|
||||
de.triggerEventHandler('mouseover', null);
|
||||
expect(icons.length).toBe(4);
|
||||
|
||||
expect(chipsComp.tipText).toBe('a')
|
||||
}));
|
||||
});
|
||||
|
||||
it('should has text-muted on icon style when field value had not authority', () => {
|
||||
const de = chipsFixture.debugElement.query(By.css('li.nav-item'));
|
||||
const icons = de.queryAll(By.css('i.fa'));
|
||||
|
||||
expect(hasClass(icons[2].nativeElement, 'text-muted')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should show tooltip on mouse over an icon', () => {
|
||||
const de = chipsFixture.debugElement.query(By.css('li.nav-item'));
|
||||
const icons = de.queryAll(By.css('i.fa'));
|
||||
|
||||
icons[0].triggerEventHandler('mouseover', null);
|
||||
|
||||
expect(chipsComp.tipText).toBe('main test')
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// declare a test component
|
||||
|
@@ -65,8 +65,7 @@ export class ChipsComponent implements OnChanges {
|
||||
}
|
||||
}
|
||||
|
||||
onDragStart(tooltip: NgbTooltip, index) {
|
||||
tooltip.close();
|
||||
onDragStart(index) {
|
||||
this.uploaderService.overrideDragOverPage();
|
||||
this.dragged = index;
|
||||
}
|
||||
@@ -77,10 +76,16 @@ export class ChipsComponent implements OnChanges {
|
||||
this.chips.updateOrder();
|
||||
}
|
||||
|
||||
showTooltip(tooltip: NgbTooltip, index, content) {
|
||||
showTooltip(tooltip: NgbTooltip, index, field?) {
|
||||
tooltip.close();
|
||||
if (!this.chips.getChipByIndex(index).editMode && this.dragged === -1) {
|
||||
this.tipText = content;
|
||||
const item = this.chips.getChipByIndex(index);
|
||||
if (!item.editMode && this.dragged === -1) {
|
||||
if (field) {
|
||||
this.tipText = item.item[field].display;
|
||||
} else {
|
||||
this.tipText = item.display;
|
||||
}
|
||||
|
||||
this.cdr.detectChanges();
|
||||
tooltip.open();
|
||||
}
|
||||
|
@@ -1,7 +1,9 @@
|
||||
import { uniqueId } from 'lodash';
|
||||
import { uniqueId, isObject } from 'lodash';
|
||||
import { isNotEmpty } from '../../empty.util';
|
||||
|
||||
export interface ChipsItemIcon {
|
||||
metadata: string;
|
||||
hasAuthority: boolean;
|
||||
style: string;
|
||||
tooltip?: any;
|
||||
}
|
||||
@@ -54,15 +56,12 @@ export class ChipsItem {
|
||||
|
||||
private setDisplayText(): void {
|
||||
let value = this.item;
|
||||
if ( typeof this.item === 'object') {
|
||||
if (isObject(this.item)) {
|
||||
// Check If displayField is in an internal object
|
||||
const obj = this.objToDisplay ? this.item[this.objToDisplay] : this.item;
|
||||
const displayFieldBkp = 'value';
|
||||
|
||||
if (obj instanceof Object && obj && obj[this.fieldToDisplay]) {
|
||||
value = obj[this.fieldToDisplay];
|
||||
} else if (obj instanceof Object && obj && obj[displayFieldBkp]) {
|
||||
value = obj[displayFieldBkp];
|
||||
if (isObject(obj) && obj) {
|
||||
value = obj[this.fieldToDisplay] || obj.value;
|
||||
} else {
|
||||
value = obj;
|
||||
}
|
||||
|
@@ -1,9 +1,7 @@
|
||||
import { findIndex, isEqual } from 'lodash';
|
||||
import { findIndex, isEqual, isObject } from 'lodash';
|
||||
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
|
||||
import { ChipsItem, ChipsItemIcon } from './chips-item.model';
|
||||
import { hasValue } from '../../empty.util';
|
||||
import { FormFieldMetadataValueObject } from '../../form/builder/models/form-field-metadata-value.model';
|
||||
import { AuthorityValueModel } from '../../../core/integration/models/authority-value.model';
|
||||
|
||||
export interface ChipsIconsConfig {
|
||||
[metadata: string]: string;
|
||||
@@ -99,16 +97,26 @@ export class Chips {
|
||||
Object.keys(item)
|
||||
.forEach((metadata) => {
|
||||
const value = item[metadata];
|
||||
if (hasValue(value)
|
||||
&& (value instanceof FormFieldMetadataValueObject || value instanceof AuthorityValueModel)
|
||||
&& ((value as FormFieldMetadataValueObject).authority || (value as AuthorityValueModel).id)
|
||||
&& this.iconsConfig.hasOwnProperty(metadata)) {
|
||||
if (hasValue(value) && isObject(value) && this.iconsConfig.hasOwnProperty(metadata)) {
|
||||
|
||||
const icon: ChipsItemIcon = {
|
||||
let icon: ChipsItemIcon;
|
||||
const hasAuthority: boolean = ((value.hasOwnProperty('authority') && value.authority)
|
||||
|| (value.hasOwnProperty('id') && value.id)) ? true : false;
|
||||
|
||||
// Set icons
|
||||
if ((this.displayObj && this.displayObj === metadata && hasAuthority)
|
||||
|| (this.displayObj && this.displayObj !== metadata)) {
|
||||
|
||||
icon = {
|
||||
metadata,
|
||||
hasAuthority: hasAuthority,
|
||||
style: this.iconsConfig[metadata]
|
||||
};
|
||||
}
|
||||
if (icon) {
|
||||
icons.push(icon);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return icons;
|
||||
|
@@ -34,14 +34,14 @@ describe('ChipsItem model test suite', () => {
|
||||
});
|
||||
|
||||
it('should update icons', () => {
|
||||
const icons: ChipsItemIcon[] = [{style: 'fa fa-plus'}];
|
||||
const icons: ChipsItemIcon[] = [{metadata: 'test', hasAuthority: false, style: 'fa fa-plus'}];
|
||||
item.updateIcons(icons);
|
||||
|
||||
expect(item.icons).toEqual(icons);
|
||||
});
|
||||
|
||||
it('should return true if has icons', () => {
|
||||
const icons: ChipsItemIcon[] = [{style: 'fa fa-plus'}];
|
||||
const icons: ChipsItemIcon[] = [{metadata: 'test', hasAuthority: false, style: 'fa fa-plus'}];
|
||||
item.updateIcons(icons);
|
||||
const hasIcons = item.hasIcons();
|
||||
|
||||
|
@@ -216,7 +216,7 @@ export abstract class FieldParser {
|
||||
controlModel.errorMessages = Object.assign(
|
||||
{},
|
||||
controlModel.errorMessages,
|
||||
{pattern: 'form.error.validation.pattern'});
|
||||
{pattern: 'error.validation.pattern'});
|
||||
|
||||
}
|
||||
|
||||
|
@@ -40,7 +40,7 @@ export class GroupFieldParser extends FieldParser {
|
||||
const mandatoryFieldEntries: FormFieldMetadataValueObject[] = this.getInitFieldValues(modelConfiguration.mandatoryField);
|
||||
mandatoryFieldEntries.forEach((entry, index) => {
|
||||
const item = Object.create(null);
|
||||
const listFields = modelConfiguration.relationFields.concat(modelConfiguration.mandatoryField);
|
||||
const listFields = [modelConfiguration.mandatoryField].concat(modelConfiguration.relationFields);
|
||||
listFields.forEach((fieldId) => {
|
||||
const value = this.getInitFieldValue(0, index, [fieldId]);
|
||||
item[fieldId] = isNotEmpty(value) ? value : PLACEHOLDER_PARENT_METADATA;
|
||||
|
Reference in New Issue
Block a user