From dd12fdf1a338c306efb2f540d00ea0f7966baf67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Kilchenmann?= Date: Wed, 26 May 2021 12:30:45 +0200 Subject: [PATCH 1/8] feat(resource): combination of two components --- src/app/app.module.ts | 2 + .../properties/properties.component.html | 126 ++++++ .../properties/properties.component.scss | 183 +++++++++ .../properties/properties.component.spec.ts | 25 ++ .../properties/properties.component.ts | 372 ++++++++++++++++++ .../resource/resource.component.html | 57 +-- .../workspace/resource/resource.component.ts | 9 - 7 files changed, 738 insertions(+), 36 deletions(-) create mode 100644 src/app/workspace/resource/properties/properties.component.html create mode 100644 src/app/workspace/resource/properties/properties.component.scss create mode 100644 src/app/workspace/resource/properties/properties.component.spec.ts create mode 100644 src/app/workspace/resource/properties/properties.component.ts diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 9cfd0e98af..20df4aa33f 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -93,6 +93,7 @@ import { ResultsComponent } from './workspace/results/results.component'; import { ResourceToolbarComponent } from './workspace/resource/resource-toolbar/resource-toolbar.component'; import { ResourcePropertiesComponent } from './workspace/resource/resource-properties/resource-properties.component'; import { StillImageComponent } from './workspace/resource/representation/still-image/still-image.component'; +import { PropertiesComponent } from './workspace/resource/properties/properties.component'; // translate: AoT requires an exported function for factories export function httpLoaderFactory(httpClient: HttpClient) { @@ -173,6 +174,7 @@ export function httpLoaderFactory(httpClient: HttpClient) { ResourceToolbarComponent, ResourcePropertiesComponent, StillImageComponent, + PropertiesComponent, ], imports: [ AppRoutingModule, diff --git a/src/app/workspace/resource/properties/properties.component.html b/src/app/workspace/resource/properties/properties.component.html new file mode 100644 index 0000000000..14574ff46a --- /dev/null +++ b/src/app/workspace/resource/properties/properties.component.html @@ -0,0 +1,126 @@ + + +
+ +

+ {{resource.res.label}} +

+ + + + + + + + + + + + + + + + + +
+ +
+
+ + +
+
+
+
+ + +
+

This resource belongs to the project + + {{project?.shortname}} + open_in_new + +

+ +

+ Created + by {{(user.username ? user.username : user.givenName + ' ' + user.familyName)}} + on {{resource.res.creationDate | date}} +

+
+ + +
+
+ + +
+ +
+
+ +

+ {{prop.propDef.label}} +

+
+
+ +
+ + +
+ +
+ + +
+ +
+ +
+
+
+
+
+ The resource {{resource?.res.resourceClassLabel}} has no defined + properties. +
diff --git a/src/app/workspace/resource/properties/properties.component.scss b/src/app/workspace/resource/properties/properties.component.scss new file mode 100644 index 0000000000..4adea67f06 --- /dev/null +++ b/src/app/workspace/resource/properties/properties.component.scss @@ -0,0 +1,183 @@ +// toolbar +.toolbar, +.infobar { + display: flex; + box-sizing: border-box; + flex-direction: row; + align-items: center; + white-space: nowrap; + padding: 0 16px; + width: 100%; + color: rgba(0, 0, 0, 0.87); +} +.toolbar { + background: whitesmoke; + + .label { + margin: 0 !important; + max-width: 48%; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } +} + +.infobar { + height: 24px; +} + +.clipboard-arkurl { + width: 264px; + height: 32px; + border: 1px solid rgba(0, 0, 0, 0.35); + border-radius: 4px 0 0 4px; +} +.ark-url-label { + margin-bottom: 8px; +} +.btn-copy-arkurl { + border-radius: 0 4px 4px 0; + background-color: rgba(0, 0, 0, 0.35); +} + + + +// +// list of properties +.border-bottom { + border-bottom: 1px solid rgba(33, 33, 33, 0.1); +} + +// smaller buttons: add value and value info +.info, +.create { + cursor: pointer; + border: none; + padding: 0em; + outline: none; + background-color: transparent; + color: #000000; +} + +.info .material-icons, +.create .material-icons { + font-size: 18px; +} + +.info .mat-icon, +.create .mat-icon { + width: 18px; + height: 18px; +} + +// properties container with property item and property value items + +.properties-container { + display: grid; + grid-template-columns: repeat(6, 1fr); + gap: 16px; + + .properties, + .incoming { + margin-top: 16px; + } + + .properties { + grid-column: 1 / span 6; + + .property { + grid-row: 1 / 1; + + display: grid; + grid-template-columns: repeat(4, 1fr); + gap: 16px; + + .property-label, + .property-value { + padding: 2px; + overflow-wrap: break-word; + } + + .property-label { + grid-column: 1 / span 1; + + .label { + text-align: right; + } + + .label-info { + cursor: help; + } + } + + .property-value { + grid-column: 2 / span 3; + } + } + } + + // incoming is not yet implemented. TODO: reactivate incoming info box + .incoming { + grid-column: 5 / span 2; + + display: grid; + gap: 16px; + grid-template-columns: 1fr 1fr; + grid-template-rows: repeat(6, minmax(60px, auto)); + + .annotations, + .links { + padding: 16px; + grid-column: 1 / span 2; + border-radius: 6px; + + ul { + padding-inline-start: 5px; + list-style-type: none; + li { + margin-bottom: 10px; + text-indent: -8px; + &:before { + content: "- "; + } + } + } + } + + .annotations { + background: rgba(245, 222, 179, 0.39); + } + + .links { + background: rgba(222, 184, 135, 0.39); + } + } +} + +.label { + color: rgba(0, 0, 0, 0.54); +} + +@media screen and (max-width: 768px) { + .properties, + .incoming { + grid-column: 1 / span 6 !important; + gap: 0 !important; + + .property { + gap: 0 !important; + } + + .annotations, + .links, + .property-label, + .property-value { + grid-column: 1 / span 4 !important; + } + } + + h3.label { + text-align: left !important; + margin: 16px 0 0 !important; + } +} diff --git a/src/app/workspace/resource/properties/properties.component.spec.ts b/src/app/workspace/resource/properties/properties.component.spec.ts new file mode 100644 index 0000000000..2c128f8742 --- /dev/null +++ b/src/app/workspace/resource/properties/properties.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { PropertiesComponent } from './properties.component'; + +describe('PropertiesComponent', () => { + let component: PropertiesComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ PropertiesComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(PropertiesComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/workspace/resource/properties/properties.component.ts b/src/app/workspace/resource/properties/properties.component.ts new file mode 100644 index 0000000000..135fdbf7a0 --- /dev/null +++ b/src/app/workspace/resource/properties/properties.component.ts @@ -0,0 +1,372 @@ +import { Component, EventEmitter, Inject, Input, OnChanges, OnDestroy, OnInit, Output } from '@angular/core'; +import { MatSnackBar } from '@angular/material/snack-bar'; +import { + ApiResponseData, + ApiResponseError, + CardinalityUtil, + Constants, + DeleteValue, + KnoraApiConnection, + PermissionUtil, + ProjectResponse, + ReadLinkValue, + ReadProject, + ReadResourceSequence, + ReadTextValueAsXml, + ReadUser, + ReadValue, + ResourcePropertyDefinition, + UserResponse +} from '@dasch-swiss/dsp-js'; +import { + AddedEventValue, + DeletedEventValue, + DspApiConnectionToken, + Events, + NotificationService, + PropertyInfoValues, + UpdatedEventValues, + UserService, + ValueOperationEventService, + ValueService +} from '@dasch-swiss/dsp-ui'; +import { Subscription } from 'rxjs'; +import { DspResource } from '../dsp-resource'; + +@Component({ + selector: 'app-properties', + templateUrl: './properties.component.html', + styleUrls: ['./properties.component.scss'] +}) +export class PropertiesComponent implements OnInit, OnChanges, OnDestroy { + + /** + * input `resource` of properties component: + * complete information about the current resource + */ + @Input() resource: DspResource; + + /** + * input `displayProjectInfo` of properties component: + * display project info or not; "This resource belongs to project XYZ" + */ + @Input() displayProjectInfo: false; + + /** + * output `referredProjectClicked` of resource view component: + * can be used to go to project page + */ + @Output() referredProjectClicked: EventEmitter = new EventEmitter(); + + /** + * output `referredProjectHovered` of resource view component: + * can be used for preview when hovering on project + */ + @Output() referredProjectHovered: EventEmitter = new EventEmitter(); + + /** + * output `referredResourceClicked` of resource view component: + * can be used to open resource + */ + @Output() referredResourceClicked: EventEmitter = new EventEmitter(); + + /** + * output `referredResourceHovered` of resource view component: + * can be used for preview when hovering on resource + */ + @Output() referredResourceHovered: EventEmitter = new EventEmitter(); + + addButtonIsVisible: boolean; // used to toggle add value button + addValueFormIsVisible: boolean; // used to toggle add value form field + propID: string; // used in template to show only the add value form of the corresponding value + + valueOperationEventSubscriptions: Subscription[] = []; // array of ValueOperationEvent subscriptions + + project: ReadProject; + user: ReadUser; + + showAllProps = false; // show or hide empty properties + + constructor( + @Inject(DspApiConnectionToken) private _dspApiConnection: KnoraApiConnection, + private _notification: NotificationService, + private _snackBar: MatSnackBar, + private _userService: UserService, + private _valueOperationEventService: ValueOperationEventService, + private _valueService: ValueService + ) { } + + ngOnInit(): void { + if (this.resource.res) { + // get user permissions + const allPermissions = PermissionUtil.allUserPermissions( + this.resource.res.userHasPermission as 'RV' | 'V' | 'M' | 'D' | 'CR' + ); + + // if user has modify permissions, set addButtonIsVisible to true so the user see's the add button + this.addButtonIsVisible = allPermissions.indexOf(PermissionUtil.Permissions.M) !== -1; + } + + // listen for the AddValue event to be emitted and call hideAddValueForm() + // this._valueOperationEventService.on(Events.ValueAdded, () => this.hideAddValueForm()) + this.valueOperationEventSubscriptions = []; + + // subscribe to the ValueOperationEventService and listen for an event to be emitted + this.valueOperationEventSubscriptions.push(this._valueOperationEventService.on( + Events.ValueAdded, (newValue: AddedEventValue) => { + if (newValue) { + this.addValueToResource(newValue.addedValue); + this.hideAddValueForm(); + } + })); + + this.valueOperationEventSubscriptions.push(this._valueOperationEventService.on( + Events.ValueUpdated, (updatedValue: UpdatedEventValues) => { + this.updateValueInResource(updatedValue.currentValue, updatedValue.updatedValue); + this.hideAddValueForm(); + })); + + this.valueOperationEventSubscriptions.push(this._valueOperationEventService.on( + Events.ValueDeleted, (deletedValue: DeletedEventValue) => this.deleteValueFromResource(deletedValue.deletedValue) + )); + } + + ngOnChanges(): void { + // get project information + this._dspApiConnection.admin.projectsEndpoint.getProjectByIri(this.resource.res.attachedToProject).subscribe( + (response: ApiResponseData) => { + this.project = response.body.project; + }, + (error: ApiResponseError) => { + this._notification.openSnackBar(error); + } + ); + + // get user information + this._userService.getUser(this.resource.res.attachedToUser).subscribe( + (response: UserResponse) => { + this.user = response.user; + } + ); + } + + ngOnDestroy() { + // unsubscribe from the event bus when component is destroyed + // if (this.valueOperationEventSubscription !== undefined) { + // this.valueOperationEventSubscription.unsubscribe(); + // } + // unsubscribe from the ValueOperationEventService when component is destroyed + if (this.valueOperationEventSubscriptions !== undefined) { + this.valueOperationEventSubscriptions.forEach(sub => sub.unsubscribe()); + } + } + + /** + * opens project + * @param linkValue + */ + openProject(project: ReadProject) { + window.open('/project/' + project.shortcode, '_blank'); + } + + previewProject(project: ReadProject) { + // --> TODO: pop up project preview on hover + } + + /** + * opens resource + * @param linkValue + */ + openResource(linkValue: ReadLinkValue) { + window.open('/resource/' + encodeURIComponent(linkValue.linkedResource.id), '_blank'); + } + + previewResource(linkValue: ReadLinkValue) { + // --> TODO: pop up resource preview on hover + } + + /** + * display message to confirm the copy of the citation link (ARK URL) + */ + openSnackBar() { + const message = 'Copied to clipboard!'; + const action = 'Citation Link'; + this._snackBar.open(message, action, { + duration: 3000, + horizontalPosition: 'center', + verticalPosition: 'top' + }); + } + + /** + * called from the template when the user clicks on the add button + */ + showAddValueForm(prop: PropertyInfoValues) { + this.propID = prop.propDef.id; + this.addValueFormIsVisible = true; + } + + /** + * called from the template when the user clicks on the cancel button + */ + hideAddValueForm() { + this.addValueFormIsVisible = false; + this.addButtonIsVisible = true; + this.propID = undefined; + } + + /** + * given a resource property, check if an add button should be displayed under the property values + * + * @param prop the resource property + */ + addValueIsAllowed(prop: PropertyInfoValues): boolean { + + // if the ontology flags this as a read-only property, + // don't ever allow to add a value + if (prop.propDef instanceof ResourcePropertyDefinition && !prop.propDef.isEditable) { + return false; + } + + const isAllowed = CardinalityUtil.createValueForPropertyAllowed( + prop.propDef.id, prop.values.length, this.resource.res.entityInfo.classes[this.resource.res.type]); + + // check if: + // cardinality allows for a value to be added + // value component does not already have an add value form open + // user has write permissions + return isAllowed && this.propID !== prop.propDef.id && this.addButtonIsVisible; + } + + /** + * updates the UI in the event of a new value being added to show the new value + * + * @param valueToAdd the value to add to the end of the values array of the filtered property + */ + addValueToResource(valueToAdd: ReadValue): void { + if (this.resource.resProps) { + this.resource.resProps + .filter(propInfoValueArray => + propInfoValueArray.propDef.id === valueToAdd.property) // filter to the correct property + .forEach(propInfoValue => + propInfoValue.values.push(valueToAdd)); // push new value to array + if (valueToAdd instanceof ReadTextValueAsXml) { + this._updateStandoffLinkValue(); + } + } else { + // --> TODO: better error handler! + console.error('No properties exist for this resource'); + } + } + + /** + * updates the UI in the event of an existing value being updated to show the updated value + * + * @param valueToReplace the value to be replaced within the values array of the filtered property + * @param updatedValue the value to replace valueToReplace with + */ + updateValueInResource(valueToReplace: ReadValue, updatedValue: ReadValue): void { + if (this.resource.resProps && updatedValue !== null) { + this.resource.resProps + .filter(propInfoValueArray => + propInfoValueArray.propDef.id === valueToReplace.property) // filter to the correct property + .forEach(filteredpropInfoValueArray => { + filteredpropInfoValueArray.values.forEach((val, index) => { // loop through each value of the current property + if (val.id === valueToReplace.id) { // find the value that should be updated using the id of valueToReplace + filteredpropInfoValueArray.values[index] = updatedValue; // replace value with the updated value + } + }); + }); + if (updatedValue instanceof ReadTextValueAsXml) { + this._updateStandoffLinkValue(); + } + } else { + console.error('No properties exist for this resource'); + } + } + + /** + * updates the UI in the event of an existing value being deleted + * + * @param valueToDelete the value to remove from the values array of the filtered property + */ + deleteValueFromResource(valueToDelete: DeleteValue): void { + if (this.resource.resProps) { + this.resource.resProps + .filter(propInfoValueArray => // filter to the correct type + this._valueService.compareObjectTypeWithValueType(propInfoValueArray.propDef.objectType, valueToDelete.type)) + .forEach(filteredpropInfoValueArray => { + filteredpropInfoValueArray.values.forEach((val, index) => { // loop through each value of the current property + if (val.id === valueToDelete.id) { // find the value that was deleted using the id + filteredpropInfoValueArray.values.splice(index, 1); // remove the value from the values array + if (val instanceof ReadTextValueAsXml) { + this._updateStandoffLinkValue(); + } + } + }); + } + ); + } else { + console.error('No properties exist for this resource'); + } + } + + /** + * updates the standoff link value for the resource being displayed. + * + */ + private _updateStandoffLinkValue(): void { + + if (this.resource.res === undefined) { + // this should never happen: + // if the user was able to click on a standoff link, + // then the resource must have been initialised before. + return; + } + + const gravsearchQuery = ` + PREFIX knora-api: + CONSTRUCT { + ?res knora-api:isMainResource true . + ?res knora-api:hasStandoffLinkTo ?target . + } WHERE { + BIND(<${this.resource.res.id}> as ?res) . + OPTIONAL { + ?res knora-api:hasStandoffLinkTo ?target . + } + } + OFFSET 0 + `; + + this._dspApiConnection.v2.search.doExtendedSearch(gravsearchQuery).subscribe( + (res: ReadResourceSequence) => { + + // one resource is expected + if (res.resources.length !== 1) { + return; + } + + const newStandoffLinkVals = res.resources[0].getValuesAs(Constants.HasStandoffLinkToValue, ReadLinkValue); + + this.resource.resProps.filter( + resPropInfoVal => (resPropInfoVal.propDef.id === Constants.HasStandoffLinkToValue) + ).forEach( + standoffLinkResPropInfoVal => { + // delete all the existing standoff link values + standoffLinkResPropInfoVal.values = []; + // push standoff link values retrieved for the resource + newStandoffLinkVals.forEach( + standoffLinkVal => { + standoffLinkResPropInfoVal.values.push(standoffLinkVal); + } + ); + }); + + }, + err => { + console.error(err); + } + ); + + } +} diff --git a/src/app/workspace/resource/resource.component.html b/src/app/workspace/resource/resource.component.html index 3f3ad2cdff..83488a1bd6 100644 --- a/src/app/workspace/resource/resource.component.html +++ b/src/app/workspace/resource/resource.component.html @@ -5,10 +5,10 @@
- + [compoundNavigation]="compoundPosition" (goToPage)="compoundNavigation($event)" + (regionClicked)="openRegion($event)"> @@ -18,35 +18,34 @@ - - - - + + - + > The resource {{resource?.res.resourceClassLabel}} has no defined - properties. + properties. --> + + - - + --> - + - + - + Annotations -
+
+ + - - + --> - +
diff --git a/src/app/workspace/resource/resource.component.ts b/src/app/workspace/resource/resource.component.ts index 84f2b807e6..19b8befdda 100644 --- a/src/app/workspace/resource/resource.component.ts +++ b/src/app/workspace/resource/resource.component.ts @@ -144,15 +144,6 @@ export class ResourceComponent implements OnInit, OnChanges, OnDestroy { // general methods // ------------------------------------------------------------------------ - // open project in new tab - openProject(project: ReadProject) { - window.open('/project/' + project.shortcode, '_blank'); - } - - openResource(linkValue: ReadLinkValue) { - window.open('/resource/' + encodeURIComponent(linkValue.linkedResource.id), '_blank'); - } - compoundNavigation(page: number) { this.selectedRegion = undefined; From 0c3290702a0abf66ecc32986418b46e783de1c9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Kilchenmann?= Date: Wed, 26 May 2021 15:21:29 +0200 Subject: [PATCH 2/8] test(resource): complete tests in properties comp --- .../properties/properties.component.spec.ts | 451 +++++++++++++++++- 1 file changed, 429 insertions(+), 22 deletions(-) diff --git a/src/app/workspace/resource/properties/properties.component.spec.ts b/src/app/workspace/resource/properties/properties.component.spec.ts index 2c128f8742..05ceb12e81 100644 --- a/src/app/workspace/resource/properties/properties.component.spec.ts +++ b/src/app/workspace/resource/properties/properties.component.spec.ts @@ -1,25 +1,432 @@ -import { ComponentFixture, TestBed } from '@angular/core/testing'; - +import { ClipboardModule } from '@angular/cdk/clipboard'; +import { Component, EventEmitter, Input, OnDestroy, OnInit, Output, ViewChild } from '@angular/core'; +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; +import { MatIconModule } from '@angular/material/icon'; +import { MatMenuModule } from '@angular/material/menu'; +import { MatSnackBarModule } from '@angular/material/snack-bar'; +import { MatTooltipModule } from '@angular/material/tooltip'; +import { By } from '@angular/platform-browser'; +import { RouterTestingModule } from '@angular/router/testing'; +import { + ApiResponseError, + Constants, + IHasPropertyWithPropertyDefinition, + KnoraApiConnection, + MockProjects, + MockResource, + MockUsers, + ProjectsEndpointAdmin, + ReadLinkValue, + ReadResource, + ReadValue, + ResourcePropertyDefinition, + SystemPropertyDefinition +} from '@dasch-swiss/dsp-js'; +import { + AppInitService, + DspActionModule, + DspApiConfigToken, + DspApiConnectionToken, + EmitEvent, + Events, + PropertyInfoValues, + UserService, + ValueOperationEventService +} from '@dasch-swiss/dsp-ui'; +import { of, Subscription } from 'rxjs'; +import { TestConfig } from 'test.config'; +import { DspResource } from '../dsp-resource'; import { PropertiesComponent } from './properties.component'; -describe('PropertiesComponent', () => { - let component: PropertiesComponent; - let fixture: ComponentFixture; - - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [ PropertiesComponent ] - }) - .compileComponents(); - }); - - beforeEach(() => { - fixture = TestBed.createComponent(PropertiesComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); +/** + * test host component to simulate parent component. + */ +@Component({ + template: ` + + ` +}) +class TestPropertyParentComponent implements OnInit, OnDestroy { + + @ViewChild('propView') propertiesComponent: PropertiesComponent; + + parentResource: DspResource; + + voeSubscriptions: Subscription[] = []; + + myNum = 0; + + constructor(public _valueOperationEventService: ValueOperationEventService) { } + + ngOnInit() { + this.voeSubscriptions.push(this._valueOperationEventService.on(Events.ValueAdded, () => this.myNum = 1)); + this.voeSubscriptions.push(this._valueOperationEventService.on(Events.ValueUpdated, () => this.myNum = 2)); + this.voeSubscriptions.push(this._valueOperationEventService.on(Events.ValueDeleted, () => this.myNum = 3)); + + MockResource.getTestThing().subscribe( + (response: ReadResource) => { + this.parentResource = new DspResource(response); + + // gather resource property information + this.parentResource.resProps = this.parentResource.res.entityInfo.classes[this.parentResource.res.type].getResourcePropertiesList().map( + (prop: IHasPropertyWithPropertyDefinition) => { + const propInfoAndValues: PropertyInfoValues = { + propDef: prop.propertyDefinition, + guiDef: prop, + values: this.parentResource.res.getValues(prop.propertyIndex) + }; + return propInfoAndValues; + } + ); + + // sort properties by guiOrder + this.parentResource.resProps.sort((a, b) => (a.guiDef.guiOrder > b.guiDef.guiOrder) ? 1 : -1); + + // get system property information + this.parentResource.systemProps = this.parentResource.res.entityInfo.getPropertyDefinitionsByType(SystemPropertyDefinition); + + }, + (error: ApiResponseError) => { + console.error('Error to get the mock resource', error); + } + ); + } + + ngOnDestroy() { + if (this.voeSubscriptions) { + this.voeSubscriptions.forEach(sub => sub.unsubscribe()); + } + } +} + +/** + * test host component to simulate child component, here display-edit. + */ +@Component({ + selector: 'dsp-display-edit', + template: '' +}) +class TestDisplayValueComponent { + + @Input() parentResource: DspResource; + @Input() displayValue: ReadValue; + @Input() configuration?: object; + + @Output() referredResourceClicked: EventEmitter = new EventEmitter(); + @Output() referredResourceHovered: EventEmitter = new EventEmitter(); + +} + +/** + * test host component to simulate child component, here add-value. + */ +@Component({ + selector: 'dsp-add-value', + template: '' +}) +class TestAddValueComponent { + + @Input() parentResource: DspResource; + @Input() resourcePropertyDefinition: ResourcePropertyDefinition; + +} + +fdescribe('PropertiesComponent', () => { + let testHostComponent: TestPropertyParentComponent; + let testHostFixture: ComponentFixture; + let voeService: ValueOperationEventService; + + beforeEach(waitForAsync(() => { + + const adminSpyObj = { + admin: { + projectsEndpoint: jasmine.createSpyObj('projectsEndpoint', ['getProjectByIri']) + } + }; + + const userServiceSpy = jasmine.createSpyObj('UserService', ['getUser']); + + TestBed.configureTestingModule({ + imports: [ + ClipboardModule, + DspActionModule, + MatIconModule, + MatMenuModule, + MatSnackBarModule, + MatTooltipModule, + RouterTestingModule + ], + declarations: [ + TestPropertyParentComponent, + TestDisplayValueComponent, + TestAddValueComponent, + PropertiesComponent + ], + providers: [ + ValueOperationEventService, + AppInitService, + { + provide: DspApiConfigToken, + useValue: TestConfig.ApiConfig + }, + { + provide: DspApiConnectionToken, + useValue: new KnoraApiConnection(TestConfig.ApiConfig) + }, + { + provide: DspApiConnectionToken, + useValue: adminSpyObj + }, + { + provide: UserService, + useValue: userServiceSpy + }, + ] + }) + .compileComponents(); + + voeService = TestBed.inject(ValueOperationEventService); + })); + + beforeEach(() => { + const adminSpy = TestBed.inject(DspApiConnectionToken); + + // mock getProjectByIri response + (adminSpy.admin.projectsEndpoint as jasmine.SpyObj).getProjectByIri.and.callFake( + () => { + const project = MockProjects.mockProject(); + return of(project); + } + ); + + // mock getUserByIri response + const userSpy = TestBed.inject(UserService); + + // mock getUserByIri response + (userSpy as jasmine.SpyObj).getUser.and.callFake( + () => { + const user = MockUsers.mockUser(); + + return of(user.body); + } + ); + + testHostFixture = TestBed.createComponent(TestPropertyParentComponent); + testHostComponent = testHostFixture.componentInstance; + testHostFixture.detectChanges(); + + expect(testHostComponent).toBeTruthy(); + }); + + it('should get the resource testding', () => { + + expect(testHostComponent.parentResource).toBeTruthy(); + expect(testHostComponent.parentResource.res.id).toEqual('http://rdfh.ch/0001/H6gBWUuJSuuO-CilHV8kQw'); + expect(testHostComponent.parentResource.res.label).toEqual('testding'); + + }); + + describe('Toolbar', () => { + let hostCompDe; + let propertyToolbarComponentDe; + + beforeEach(() => { + expect(testHostComponent.propertiesComponent).toBeTruthy(); + + hostCompDe = testHostFixture.debugElement; + + propertyToolbarComponentDe = hostCompDe.query(By.directive(PropertiesComponent)); + + expect(testHostComponent).toBeTruthy(); + + testHostFixture.detectChanges(); + }); + + it('should have the label "testding"', () => { + const resLabelDebugElement = propertyToolbarComponentDe.query(By.css('h3.label')); + const resLabelNativeElement = resLabelDebugElement.nativeElement; + + expect(resLabelNativeElement.textContent.trim()).toBe('testding'); + }); + + it('should toggle list of properties', () => { + const resLabelDebugElement = propertyToolbarComponentDe.query(By.css('button.toggle-props')); + const resLabelNativeElement = resLabelDebugElement.nativeElement; + // the button contains an icon "unfold_more" and the text "Increase properties" + expect(resLabelNativeElement.textContent.trim()).toBe('unfold_moreShow all properties'); + + resLabelNativeElement.click(); + + testHostFixture.detectChanges(); + + // the button contains an icon "unfold_less" and the text "Decrease properties" + expect(resLabelNativeElement.textContent.trim()).toBe('unfold_lessHide empty properties'); + + }); + }); + + // --> TODO: currently not possible to test copy to clipboard from Material Angular + // https://stackoverflow.com/questions/60337742/test-copy-to-clipboard-function + + describe('List of properties', () => { + it('should get 25 properties', () => { + + expect(testHostComponent.parentResource.resProps).toBeTruthy(); + expect(testHostComponent.parentResource.resProps.length).toBe(25); + + }); + + it('should get the resource testding', () => { + + expect(testHostComponent.parentResource.res).toBeTruthy(); + expect(testHostComponent.parentResource.res.id).toEqual('http://rdfh.ch/0001/H6gBWUuJSuuO-CilHV8kQw'); + expect(testHostComponent.parentResource.res.label).toEqual('testding'); + + }); + + it('should display a text value among the property list', () => { + + expect(testHostComponent.parentResource.resProps[4].propDef.label).toEqual('Text'); + expect(testHostComponent.parentResource.resProps[4].propDef.comment).toBe(undefined); + expect(testHostComponent.parentResource.resProps[4].guiDef.cardinality).toEqual(2); + expect(testHostComponent.parentResource.resProps[4].guiDef.guiOrder).toEqual(2); + expect(testHostComponent.parentResource.resProps[4].values[0].type).toEqual('http://api.knora.org/ontology/knora-api/v2#TextValue'); + + }); + + it('should get some system properties', () => { + + expect(testHostComponent.parentResource.systemProps).toBeTruthy(); + expect(testHostComponent.parentResource.systemProps.length).toEqual(13); + + // check if the first system property is an ARK url + expect(testHostComponent.parentResource.systemProps[0].label).toEqual('ARK URL'); + + }); + + it('should trigger the callback when an event is emitted', () => { + + expect(testHostComponent.myNum).toEqual(0); + + voeService.emit(new EmitEvent(Events.ValueAdded)); + + expect(testHostComponent.myNum).toEqual(1); + }); + + it('should unsubscribe from changes when destroyed', () => { + testHostComponent.voeSubscriptions.forEach(sub => { + expect(sub.closed).toBe(false); + }); + + testHostFixture.destroy(); + + testHostComponent.voeSubscriptions.forEach(sub => { + expect(sub.closed).toBe(true); + }); + }); + }); + + describe('Add value', () => { + let hostCompDe; + let propertyViewComponentDe; + + beforeEach(() => { + expect(testHostComponent.propertiesComponent).toBeTruthy(); + + hostCompDe = testHostFixture.debugElement; + + propertyViewComponentDe = hostCompDe.query(By.directive(PropertiesComponent)); + + expect(testHostComponent).toBeTruthy(); + + testHostComponent.propertiesComponent.addButtonIsVisible = true; + testHostComponent.propertiesComponent.addValueFormIsVisible = false; + testHostFixture.detectChanges(); + }); + + it('should show an add button under each property that has a value component and value and appropriate cardinality', () => { + const addButtons = propertyViewComponentDe.queryAll(By.css('button.create')); + expect(addButtons.length).toEqual(14); + + }); + + it('should show an add button under a property with a cardinality of 1 and does not have a value', () => { + + // show all properties so that we can access properties with no values + const toggleAllPropsElement = testHostFixture.debugElement.query(By.css('button.toggle-props')); + toggleAllPropsElement.nativeElement.click(); + + testHostFixture.detectChanges(); + + let addButtons = propertyViewComponentDe.queryAll(By.css('button.create')); + + // current amount of buttons should equal 17 + // because the boolean property shouldn't have an add button if it has a value + // standoff links value and has incoming link value are system props and cannot be added: -2 + expect(addButtons.length).toEqual(17); + + // remove value from the boolean property + testHostComponent.parentResource.resProps[9].values = []; + + testHostFixture.detectChanges(); + + // now the boolean property should have an add button + // so the amount of add buttons on the page should increase by 1 + // standoff links value and has incoming link value are system props and cannot be added: -2 + addButtons = propertyViewComponentDe.queryAll(By.css('button.create')); + expect(addButtons.length).toEqual(18); + + }); + + it('should show an add value component when the add button is clicked', () => { + const addButtonDebugElement = propertyViewComponentDe.query(By.css('button.create')); + const addButtonNativeElement = addButtonDebugElement.nativeElement; + + expect(propertyViewComponentDe.query(By.css('.add-value'))).toBeNull(); + + addButtonNativeElement.click(); + + testHostFixture.detectChanges(); + + const addButtons = propertyViewComponentDe.queryAll(By.css('button.create')); + + // the add button for the property with the open add value form is hidden + expect(addButtons.length).toEqual(13); + + expect(propertyViewComponentDe.query(By.css('.add-value'))).toBeDefined(); + + }); + + it('should determine that adding a standoff link value is not allowed', () => { + + const standoffLinkVal = testHostComponent.parentResource.resProps.filter( + propVal => propVal.propDef.id === Constants.HasStandoffLinkToValue + ); + + expect(testHostComponent.propertiesComponent.addValueIsAllowed(standoffLinkVal[0])).toBeFalsy(); + + }); + + it('should determine that adding a incoming link value is not allowed', () => { + + const standoffLinkVal = testHostComponent.parentResource.resProps.filter( + propVal => propVal.propDef.id === 'http://api.knora.org/ontology/knora-api/v2#hasIncomingLinkValue' + ); + + expect(testHostComponent.propertiesComponent.addValueIsAllowed(standoffLinkVal[0])).toBeFalsy(); + + }); + + it('should determine that adding an int value is allowed', () => { + + const standoffLinkVal = testHostComponent.parentResource.resProps.filter( + propVal => propVal.propDef.id === 'http://0.0.0.0:3333/ontology/0001/anything/v2#hasInteger' + ); + + expect(testHostComponent.propertiesComponent.addValueIsAllowed(standoffLinkVal[0])).toBeTruthy(); + + }); + + }); }); From 2eed04c5fd9664b80098af54e2b87a1f36c86263 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Kilchenmann?= Date: Wed, 26 May 2021 15:27:45 +0200 Subject: [PATCH 3/8] refactor(resource): delete unused components --- src/app/app.module.ts | 8 +- .../resource-properties.component.html | 53 --- .../resource-properties.component.scss | 137 ------- .../resource-properties.component.spec.ts | 383 ------------------ .../resource-properties.component.ts | 307 -------------- .../resource-toolbar.component.html | 71 ---- .../resource-toolbar.component.scss | 37 -- .../resource-toolbar.component.spec.ts | 182 --------- .../resource-toolbar.component.ts | 86 ---- 9 files changed, 2 insertions(+), 1262 deletions(-) delete mode 100644 src/app/workspace/resource/resource-properties/resource-properties.component.html delete mode 100644 src/app/workspace/resource/resource-properties/resource-properties.component.scss delete mode 100644 src/app/workspace/resource/resource-properties/resource-properties.component.spec.ts delete mode 100644 src/app/workspace/resource/resource-properties/resource-properties.component.ts delete mode 100644 src/app/workspace/resource/resource-toolbar/resource-toolbar.component.html delete mode 100644 src/app/workspace/resource/resource-toolbar/resource-toolbar.component.scss delete mode 100644 src/app/workspace/resource/resource-toolbar/resource-toolbar.component.spec.ts delete mode 100644 src/app/workspace/resource/resource-toolbar/resource-toolbar.component.ts diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 20df4aa33f..292b2f0186 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -90,8 +90,6 @@ import { SwitchPropertiesComponent } from './workspace/resource/resource-instanc import { SelectResourceClassComponent } from './workspace/resource/resource-instance-form/select-resource-class/select-resource-class.component'; import { ResourceComponent } from './workspace/resource/resource.component'; import { ResultsComponent } from './workspace/results/results.component'; -import { ResourceToolbarComponent } from './workspace/resource/resource-toolbar/resource-toolbar.component'; -import { ResourcePropertiesComponent } from './workspace/resource/resource-properties/resource-properties.component'; import { StillImageComponent } from './workspace/resource/representation/still-image/still-image.component'; import { PropertiesComponent } from './workspace/resource/properties/properties.component'; @@ -147,6 +145,7 @@ export function httpLoaderFactory(httpClient: HttpClient) { ProjectsComponent, ProjectsListComponent, ProjectTabViewComponent, + PropertiesComponent, PropertyFormComponent, PropertyInfoComponent, ResourceClassFormComponent, @@ -161,6 +160,7 @@ export function httpLoaderFactory(httpClient: HttpClient) { SelectProjectComponent, SelectPropertiesComponent, SelectResourceClassComponent, + StillImageComponent, SwitchPropertiesComponent, SystemComponent, TermsTabViewComponent, @@ -171,10 +171,6 @@ export function httpLoaderFactory(httpClient: HttpClient) { UsersComponent, UsersListComponent, VisualizerComponent, - ResourceToolbarComponent, - ResourcePropertiesComponent, - StillImageComponent, - PropertiesComponent, ], imports: [ AppRoutingModule, diff --git a/src/app/workspace/resource/resource-properties/resource-properties.component.html b/src/app/workspace/resource/resource-properties/resource-properties.component.html deleted file mode 100644 index ebdc0abcab..0000000000 --- a/src/app/workspace/resource/resource-properties/resource-properties.component.html +++ /dev/null @@ -1,53 +0,0 @@ - -
-
- - -
- -
-
- -

- {{prop.propDef.label}} -

-
-
- -
- - -
- -
- - -
- -
- -
-
-
-
-
-
diff --git a/src/app/workspace/resource/resource-properties/resource-properties.component.scss b/src/app/workspace/resource/resource-properties/resource-properties.component.scss deleted file mode 100644 index 0de3b07816..0000000000 --- a/src/app/workspace/resource/resource-properties/resource-properties.component.scss +++ /dev/null @@ -1,137 +0,0 @@ -.border-bottom { - border-bottom: 1px solid rgba(33,33,33,.1); -} - -// smaller buttons: add value and value info -.info, -.create { - cursor: pointer; - border: none; - padding: 0em; - outline: none; - background-color: transparent; - color: #000000; -} - -.info .material-icons, -.create .material-icons { - font-size: 18px; -} - -.info .mat-icon, -.create .mat-icon { - width: 18px; - height: 18px; -} - -// properties container with property item and property value items - -.properties-container { - display: grid; - grid-template-columns: repeat(6, 1fr); - gap: 16px; - - .properties, - .incoming { - margin-top: 16px; - } - - .properties { - grid-column: 1 / span 6; - - .property { - grid-row: 1 / 1; - - display: grid; - grid-template-columns: repeat(4, 1fr); - gap: 16px; - - .property-label, - .property-value { - padding: 2px; - overflow-wrap: break-word; - } - - .property-label { - grid-column: 1 / span 1; - - .label { - text-align: right; - } - - .label-info { - cursor: help; - } - } - - .property-value { - grid-column: 2 / span 3; - } - } - } - - // incoming is not yet implemented. TODO: reactivate incoming info box - .incoming { - grid-column: 5 / span 2; - - display: grid; - gap: 16px; - grid-template-columns: 1fr 1fr; - grid-template-rows: repeat(6, minmax(60px, auto)); - - .annotations, - .links { - padding: 16px; - grid-column: 1 / span 2; - border-radius: 6px; - - ul { - padding-inline-start: 5px; - list-style-type: none; - li { - margin-bottom: 10px; - text-indent: -8px; - &:before { - content: "- "; - } - } - } - } - - .annotations { - background: rgba(245, 222, 179, 0.39); - } - - .links { - background: rgba(222, 184, 135, 0.39); - } - } -} - -.label { - color: rgba(0, 0, 0, 0.54); -} - -@media screen and (max-width: 768px) { - .properties, - .incoming { - grid-column: 1 / span 6 !important; - gap: 0 !important; - - .property { - gap: 0 !important; - } - - .annotations, - .links, - .property-label, - .property-value { - grid-column: 1 / span 4 !important; - } - } - - h3.label { - text-align: left !important; - margin: 16px 0 0 !important; - } -} diff --git a/src/app/workspace/resource/resource-properties/resource-properties.component.spec.ts b/src/app/workspace/resource/resource-properties/resource-properties.component.spec.ts deleted file mode 100644 index 70c4e67127..0000000000 --- a/src/app/workspace/resource/resource-properties/resource-properties.component.spec.ts +++ /dev/null @@ -1,383 +0,0 @@ -import { Component, EventEmitter, Input, OnDestroy, OnInit, Output, ViewChild } from '@angular/core'; -import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; -import { MatIconModule } from '@angular/material/icon'; -import { MatTooltipModule } from '@angular/material/tooltip'; -import { By } from '@angular/platform-browser'; -import { RouterTestingModule } from '@angular/router/testing'; -import { - ApiResponseError, - Constants, - IHasPropertyWithPropertyDefinition, - KnoraApiConnection, - MockResource, - ReadLinkValue, - ReadResource, - ReadValue, - ResourcePropertyDefinition, - SystemPropertyDefinition -} from '@dasch-swiss/dsp-js'; -import { - AppInitService, - DspActionModule, - DspApiConfigToken, - DspApiConnectionToken, - EmitEvent, - Events, - PropertyInfoValues, - ValueOperationEventService -} from '@dasch-swiss/dsp-ui'; -import { Subscription } from 'rxjs'; -import { TestConfig } from 'test.config'; -import { ResourcePropertiesComponent } from './resource-properties.component'; - -/** - * test host component to simulate parent component. - */ -@Component({ - template: ` - - ` -}) -class TestPropertyParentComponent implements OnInit, OnDestroy { - - @ViewChild('propView') resourcePropertiesComponent: ResourcePropertiesComponent; - - parentResource: ReadResource; - - propArray: PropertyInfoValues[] = []; - - systemPropArray: SystemPropertyDefinition[] = []; - - showAllProps = false; - - voeSubscriptions: Subscription[] = []; - - myNum = 0; - - linkValClicked: ReadLinkValue; - - linkValHovered: ReadLinkValue; - - constructor(public _valueOperationEventService: ValueOperationEventService) { } - - ngOnInit() { - this.voeSubscriptions.push(this._valueOperationEventService.on(Events.ValueAdded, () => this.myNum = 1)); - this.voeSubscriptions.push(this._valueOperationEventService.on(Events.ValueUpdated, () => this.myNum = 2)); - this.voeSubscriptions.push(this._valueOperationEventService.on(Events.ValueDeleted, () => this.myNum = 3)); - - MockResource.getTestThing().subscribe( - (response: ReadResource) => { - this.parentResource = response; - - // gather resource property information - this.propArray = this.parentResource.entityInfo.classes[this.parentResource.type].getResourcePropertiesList().map( - (prop: IHasPropertyWithPropertyDefinition) => { - const propInfoAndValues: PropertyInfoValues = { - propDef: prop.propertyDefinition, - guiDef: prop, - values: this.parentResource.getValues(prop.propertyIndex) - }; - return propInfoAndValues; - } - ); - - // sort properties by guiOrder - this.propArray.sort((a, b) => (a.guiDef.guiOrder > b.guiDef.guiOrder) ? 1 : -1); - - // get system property information - this.systemPropArray = this.parentResource.entityInfo.getPropertyDefinitionsByType(SystemPropertyDefinition); - - }, - (error: ApiResponseError) => { - console.error('Error to get the mock resource', error); - } - ); - } - - ngOnDestroy() { - if (this.voeSubscriptions) { - this.voeSubscriptions.forEach(sub => sub.unsubscribe()); - } - } - - internalLinkClicked(linkVal: ReadLinkValue) { - this.linkValClicked = linkVal; - } - - internalLinkHovered(linkVal: ReadLinkValue) { - this.linkValHovered = linkVal; - } -} - -/** - * test host component to simulate child component, here display-edit. - */ -@Component({ - selector: 'dsp-display-edit', - template: '' -}) -class TestDisplayValueComponent { - - @Input() parentResource: ReadResource; - @Input() displayValue: ReadValue; - @Input() propArray: PropertyInfoValues[]; - @Input() configuration?: object; - - @Output() referredResourceClicked: EventEmitter = new EventEmitter(); - @Output() referredResourceHovered: EventEmitter = new EventEmitter(); - -} - -/** - * test host component to simulate child component, here add-value. - */ -@Component({ - selector: 'dsp-add-value', - template: '' -}) -class TestAddValueComponent { - - @Input() parentResource: ReadResource; - @Input() resourcePropertyDefinition: ResourcePropertyDefinition; - -} - -describe('ResourcePropertiesComponent', () => { - let testHostComponent: TestPropertyParentComponent; - let testHostFixture: ComponentFixture; - let voeService: ValueOperationEventService; - - beforeEach(waitForAsync(() => { - TestBed.configureTestingModule({ - imports: [ - DspActionModule, - MatIconModule, - MatTooltipModule, - RouterTestingModule - ], - declarations: [ - TestPropertyParentComponent, - TestDisplayValueComponent, - TestAddValueComponent, - ResourcePropertiesComponent - ], - providers: [ - ValueOperationEventService, - AppInitService, - { - provide: DspApiConfigToken, - useValue: TestConfig.ApiConfig - }, - { - provide: DspApiConnectionToken, - useValue: new KnoraApiConnection(TestConfig.ApiConfig) - } - ] - }) - .compileComponents(); - - voeService = TestBed.inject(ValueOperationEventService); - })); - - beforeEach(() => { - testHostFixture = TestBed.createComponent(TestPropertyParentComponent); - testHostComponent = testHostFixture.componentInstance; - testHostFixture.detectChanges(); - - expect(testHostComponent).toBeTruthy(); - }); - - - it('should get 25 properties', () => { - - expect(testHostComponent.propArray).toBeTruthy(); - expect(testHostComponent.propArray.length).toBe(25); - - }); - - it('should get the resource testding', () => { - - expect(testHostComponent.parentResource).toBeTruthy(); - expect(testHostComponent.parentResource.id).toEqual('http://rdfh.ch/0001/H6gBWUuJSuuO-CilHV8kQw'); - expect(testHostComponent.parentResource.label).toEqual('testding'); - - }); - - it('should display a text value among the property list', () => { - - expect(testHostComponent.propArray[4].propDef.label).toEqual('Text'); - expect(testHostComponent.propArray[4].propDef.comment).toBe(undefined); - expect(testHostComponent.propArray[4].guiDef.cardinality).toEqual(2); - expect(testHostComponent.propArray[4].guiDef.guiOrder).toEqual(2); - expect(testHostComponent.propArray[4].values[0].type).toEqual('http://api.knora.org/ontology/knora-api/v2#TextValue'); - - }); - - it('should get some system properties', () => { - - expect(testHostComponent.systemPropArray).toBeTruthy(); - expect(testHostComponent.systemPropArray.length).toEqual(13); - - // check if the first system property is an ARK url - expect(testHostComponent.systemPropArray[0].label).toEqual('ARK URL'); - - }); - - it('should propagate a click event on a link value', () => { - - const displayEdit = testHostFixture.debugElement.query(By.directive(TestDisplayValueComponent)); - - const linkVal = new ReadLinkValue(); - linkVal.linkedResourceIri = 'testIri'; - - expect(testHostComponent.linkValClicked).toBeUndefined(); - - (displayEdit.componentInstance as TestDisplayValueComponent).referredResourceClicked.emit(linkVal); - - expect(testHostComponent.linkValClicked.linkedResourceIri).toEqual('testIri'); - - }); - - it('should propagate a hover event on a link value', () => { - - const displayEdit = testHostFixture.debugElement.query(By.directive(TestDisplayValueComponent)); - - const linkVal = new ReadLinkValue(); - linkVal.linkedResourceIri = 'testIri'; - - expect(testHostComponent.linkValHovered).toBeUndefined(); - - (displayEdit.componentInstance as TestDisplayValueComponent).referredResourceHovered.emit(linkVal); - - expect(testHostComponent.linkValHovered.linkedResourceIri).toEqual('testIri'); - - }); - - it('should trigger the callback when an event is emitted', () => { - - expect(testHostComponent.myNum).toEqual(0); - - voeService.emit(new EmitEvent(Events.ValueAdded)); - - expect(testHostComponent.myNum).toEqual(1); - }); - - it('should unsubscribe from changes when destroyed', () => { - testHostComponent.voeSubscriptions.forEach(sub => { - expect(sub.closed).toBe(false); - }); - - testHostFixture.destroy(); - - testHostComponent.voeSubscriptions.forEach(sub => { - expect(sub.closed).toBe(true); - }); - }); - - describe('Add value', () => { - let hostCompDe; - let propertyViewComponentDe; - - beforeEach(() => { - expect(testHostComponent.resourcePropertiesComponent).toBeTruthy(); - - hostCompDe = testHostFixture.debugElement; - - propertyViewComponentDe = hostCompDe.query(By.directive(ResourcePropertiesComponent)); - - expect(testHostComponent).toBeTruthy(); - - testHostComponent.resourcePropertiesComponent.addButtonIsVisible = true; - testHostComponent.resourcePropertiesComponent.addValueFormIsVisible = false; - testHostFixture.detectChanges(); - }); - - it('should show an add button under each property that has a value component and value and appropriate cardinality', () => { - const addButtons = propertyViewComponentDe.queryAll(By.css('button.create')); - expect(addButtons.length).toEqual(14); - - }); - - it('should show an add button under a property with a cardinality of 1 and does not have a value', () => { - - // show all properties so that we can access properties with no values - testHostComponent.showAllProps = true; - testHostFixture.detectChanges(); - - let addButtons = propertyViewComponentDe.queryAll(By.css('button.create')); - - // current amount of buttons should equal 17 - // because the boolean property shouldn't have an add button if it has a value - // standoff links value and has incoming link value are system props and cannot be added: -2 - expect(addButtons.length).toEqual(17); - - // remove value from the boolean property - testHostComponent.propArray[9].values = []; - - testHostFixture.detectChanges(); - - // now the boolean property should have an add button - // so the amount of add buttons on the page should increase by 1 - // standoff links value and has incoming link value are system props and cannot be added: -2 - addButtons = propertyViewComponentDe.queryAll(By.css('button.create')); - expect(addButtons.length).toEqual(18); - - }); - - it('should show an add value component when the add button is clicked', () => { - const addButtonDebugElement = propertyViewComponentDe.query(By.css('button.create')); - const addButtonNativeElement = addButtonDebugElement.nativeElement; - - expect(propertyViewComponentDe.query(By.css('.add-value'))).toBeNull(); - - addButtonNativeElement.click(); - - testHostFixture.detectChanges(); - - const addButtons = propertyViewComponentDe.queryAll(By.css('button.create')); - - // the add button for the property with the open add value form is hidden - expect(addButtons.length).toEqual(13); - - expect(propertyViewComponentDe.query(By.css('.add-value'))).toBeDefined(); - - }); - - it('should determine that adding a standoff link value is not allowed', () => { - - const standoffLinkVal = testHostComponent.propArray.filter( - propVal => propVal.propDef.id === Constants.HasStandoffLinkToValue - ); - - expect(testHostComponent.resourcePropertiesComponent.addValueIsAllowed(standoffLinkVal[0])).toBeFalsy(); - - }); - - it('should determine that adding a incoming link value is not allowed', () => { - - const standoffLinkVal = testHostComponent.propArray.filter( - propVal => propVal.propDef.id === 'http://api.knora.org/ontology/knora-api/v2#hasIncomingLinkValue' - ); - - expect(testHostComponent.resourcePropertiesComponent.addValueIsAllowed(standoffLinkVal[0])).toBeFalsy(); - - }); - - it('should determine that adding an int value is allowed', () => { - - const standoffLinkVal = testHostComponent.propArray.filter( - propVal => propVal.propDef.id === 'http://0.0.0.0:3333/ontology/0001/anything/v2#hasInteger' - ); - - expect(testHostComponent.resourcePropertiesComponent.addValueIsAllowed(standoffLinkVal[0])).toBeTruthy(); - - }); - - }); -}); diff --git a/src/app/workspace/resource/resource-properties/resource-properties.component.ts b/src/app/workspace/resource/resource-properties/resource-properties.component.ts deleted file mode 100644 index 57b5299686..0000000000 --- a/src/app/workspace/resource/resource-properties/resource-properties.component.ts +++ /dev/null @@ -1,307 +0,0 @@ -import { - Component, EventEmitter, - Inject, - Input, - OnDestroy, - OnInit, - Output, - ViewChild -} from '@angular/core'; -import { - CardinalityUtil, - Constants, - DeleteValue, - KnoraApiConnection, - PermissionUtil, - ReadLinkValue, - ReadResource, - ReadResourceSequence, - ReadTextValueAsXml, - ReadValue, - ResourcePropertyDefinition, - SystemPropertyDefinition -} from '@dasch-swiss/dsp-js'; -import { - AddedEventValue, - AddValueComponent, - DeletedEventValue, - DisplayEditComponent, - DspApiConnectionToken, - Events, - PropertyInfoValues, - UpdatedEventValues, - ValueOperationEventService, - ValueService -} from '@dasch-swiss/dsp-ui'; -import { Subscription } from 'rxjs'; - -@Component({ - selector: 'app-resource-properties', - templateUrl: './resource-properties.component.html', - styleUrls: ['./resource-properties.component.scss'] -}) -export class ResourcePropertiesComponent implements OnInit, OnDestroy { - - @ViewChild('displayEdit') displayEditComponent: DisplayEditComponent; - @ViewChild('addValue') addValueComponent: AddValueComponent; - /** - * parent resource - * - * @param (parentResource) - */ - @Input() parentResource: ReadResource; - - /** - * array of property object with ontology class prop def, list of properties and corresponding values - * - * @param (propArray) - */ - @Input() propArray: PropertyInfoValues[]; - - /** - * array of system property object with list of system properties - * - * @param (systemPropArray) - */ - @Input() systemPropArray: SystemPropertyDefinition[]; - - /** - * show all properties, even if they don't have a value. - * - * @param (showAllProps) - */ - @Input() showAllProps = false; - - @Output() referredResourceClicked: EventEmitter = new EventEmitter(); - - @Output() referredResourceHovered: EventEmitter = new EventEmitter(); - - addButtonIsVisible: boolean; // used to toggle add value button - addValueFormIsVisible: boolean; // used to toggle add value form field - propID: string; // used in template to show only the add value form of the corresponding value - - valueOperationEventSubscriptions: Subscription[] = []; // array of ValueOperationEvent subscriptions - - constructor( - @Inject(DspApiConnectionToken) private _dspApiConnection: KnoraApiConnection, - private _valueOperationEventService: ValueOperationEventService, - private _valueService: ValueService - ) { } - - ngOnInit() { - if (this.parentResource) { - // get user permissions - const allPermissions = PermissionUtil.allUserPermissions( - this.parentResource.userHasPermission as 'RV' | 'V' | 'M' | 'D' | 'CR' - ); - - // if user has modify permissions, set addButtonIsVisible to true so the user see's the add button - this.addButtonIsVisible = allPermissions.indexOf(PermissionUtil.Permissions.M) !== -1; - } - - // listen for the AddValue event to be emitted and call hideAddValueForm() - // this._valueOperationEventService.on(Events.ValueAdded, () => this.hideAddValueForm()) - this.valueOperationEventSubscriptions = []; - - // subscribe to the ValueOperationEventService and listen for an event to be emitted - this.valueOperationEventSubscriptions.push(this._valueOperationEventService.on( - Events.ValueAdded, (newValue: AddedEventValue) => { - if (newValue) { - this.addValueToResource(newValue.addedValue); - this.hideAddValueForm(); - } - })); - - this.valueOperationEventSubscriptions.push(this._valueOperationEventService.on( - Events.ValueUpdated, (updatedValue: UpdatedEventValues) => { - this.updateValueInResource(updatedValue.currentValue, updatedValue.updatedValue); - this.hideAddValueForm(); - })); - - this.valueOperationEventSubscriptions.push(this._valueOperationEventService.on( - Events.ValueDeleted, (deletedValue: DeletedEventValue) => this.deleteValueFromResource(deletedValue.deletedValue) - )); - } - - ngOnDestroy() { - // unsubscribe from the event bus when component is destroyed - // if (this.valueOperationEventSubscription !== undefined) { - // this.valueOperationEventSubscription.unsubscribe(); - // } - // unsubscribe from the ValueOperationEventService when component is destroyed - if (this.valueOperationEventSubscriptions !== undefined) { - this.valueOperationEventSubscriptions.forEach(sub => sub.unsubscribe()); - } - } - - /** - * called from the template when the user clicks on the add button - */ - showAddValueForm(prop: PropertyInfoValues) { - this.propID = prop.propDef.id; - this.addValueFormIsVisible = true; - } - - /** - * called from the template when the user clicks on the cancel button - */ - hideAddValueForm() { - this.addValueFormIsVisible = false; - this.addButtonIsVisible = true; - this.propID = undefined; - } - - /** - * given a resource property, check if an add button should be displayed under the property values - * - * @param prop the resource property - */ - addValueIsAllowed(prop: PropertyInfoValues): boolean { - - // if the ontology flags this as a read-only property, - // don't ever allow to add a value - if (prop.propDef instanceof ResourcePropertyDefinition && !prop.propDef.isEditable) { - return false; - } - - const isAllowed = CardinalityUtil.createValueForPropertyAllowed( - prop.propDef.id, prop.values.length, this.parentResource.entityInfo.classes[this.parentResource.type]); - - // check if: - // cardinality allows for a value to be added - // value component does not already have an add value form open - // user has write permissions - return isAllowed && this.propID !== prop.propDef.id && this.addButtonIsVisible; - } - - /** - * updates the UI in the event of a new value being added to show the new value - * - * @param valueToAdd the value to add to the end of the values array of the filtered property - */ - addValueToResource(valueToAdd: ReadValue): void { - if (this.propArray) { - this.propArray - .filter(propInfoValueArray => - propInfoValueArray.propDef.id === valueToAdd.property) // filter to the correct property - .forEach(propInfoValue => - propInfoValue.values.push(valueToAdd)); // push new value to array - if (valueToAdd instanceof ReadTextValueAsXml) { - this._updateStandoffLinkValue(); - } - } else { - console.error('No properties exist for this resource'); - } - } - - /** - * updates the UI in the event of an existing value being updated to show the updated value - * - * @param valueToReplace the value to be replaced within the values array of the filtered property - * @param updatedValue the value to replace valueToReplace with - */ - updateValueInResource(valueToReplace: ReadValue, updatedValue: ReadValue): void { - if (this.propArray && updatedValue !== null) { - this.propArray - .filter(propInfoValueArray => - propInfoValueArray.propDef.id === valueToReplace.property) // filter to the correct property - .forEach(filteredpropInfoValueArray => { - filteredpropInfoValueArray.values.forEach((val, index) => { // loop through each value of the current property - if (val.id === valueToReplace.id) { // find the value that should be updated using the id of valueToReplace - filteredpropInfoValueArray.values[index] = updatedValue; // replace value with the updated value - } - }); - }); - if (updatedValue instanceof ReadTextValueAsXml) { - this._updateStandoffLinkValue(); - } - } else { - console.error('No properties exist for this resource'); - } - } - - /** - * updates the UI in the event of an existing value being deleted - * - * @param valueToDelete the value to remove from the values array of the filtered property - */ - deleteValueFromResource(valueToDelete: DeleteValue): void { - if (this.propArray) { - this.propArray - .filter(propInfoValueArray => // filter to the correct type - this._valueService.compareObjectTypeWithValueType(propInfoValueArray.propDef.objectType, valueToDelete.type)) - .forEach(filteredpropInfoValueArray => { - filteredpropInfoValueArray.values.forEach((val, index) => { // loop through each value of the current property - if (val.id === valueToDelete.id) { // find the value that was deleted using the id - filteredpropInfoValueArray.values.splice(index, 1); // remove the value from the values array - if (val instanceof ReadTextValueAsXml) { - this._updateStandoffLinkValue(); - } - } - }); - } - ); - } else { - console.error('No properties exist for this resource'); - } - } - - /** - * updates the standoff link value for the resource being displayed. - * - */ - private _updateStandoffLinkValue(): void { - - if (this.parentResource === undefined) { - // this should never happen: - // if the user was able to click on a standoff link, - // then the resource must have been initialised before. - return; - } - - const gravsearchQuery = ` - PREFIX knora-api: - CONSTRUCT { - ?res knora-api:isMainResource true . - ?res knora-api:hasStandoffLinkTo ?target . - } WHERE { - BIND(<${this.parentResource.id}> as ?res) . - OPTIONAL { - ?res knora-api:hasStandoffLinkTo ?target . - } - } - OFFSET 0 - `; - - this._dspApiConnection.v2.search.doExtendedSearch(gravsearchQuery).subscribe( - (res: ReadResourceSequence) => { - - // one resource is expected - if (res.resources.length !== 1) { - return; - } - - const newStandoffLinkVals = res.resources[0].getValuesAs(Constants.HasStandoffLinkToValue, ReadLinkValue); - - this.propArray.filter( - resPropInfoVal => (resPropInfoVal.propDef.id === Constants.HasStandoffLinkToValue) - ).forEach( - standoffLinkResPropInfoVal => { - // delete all the existing standoff link values - standoffLinkResPropInfoVal.values = []; - // push standoff link values retrieved for the resource - newStandoffLinkVals.forEach( - standoffLinkVal => { - standoffLinkResPropInfoVal.values.push(standoffLinkVal); - } - ); - }); - - }, - err => { - console.error(err); - } - ); - - } -} diff --git a/src/app/workspace/resource/resource-toolbar/resource-toolbar.component.html b/src/app/workspace/resource/resource-toolbar/resource-toolbar.component.html deleted file mode 100644 index 6846fc0dd6..0000000000 --- a/src/app/workspace/resource/resource-toolbar/resource-toolbar.component.html +++ /dev/null @@ -1,71 +0,0 @@ - -
- -

- {{resource.label}} -

- - - - - - - - - - - - - - - - - -
- -
-
- - -
-
- - -
- -
- -
-

This resource belongs to the project - - {{project?.shortname}} - open_in_new - -

- -

- Created - by {{(user.username ? user.username : user.givenName + ' ' + user.familyName)}} - on {{resource.creationDate | date}} -

-
diff --git a/src/app/workspace/resource/resource-toolbar/resource-toolbar.component.scss b/src/app/workspace/resource/resource-toolbar/resource-toolbar.component.scss deleted file mode 100644 index f96e2bda13..0000000000 --- a/src/app/workspace/resource/resource-toolbar/resource-toolbar.component.scss +++ /dev/null @@ -1,37 +0,0 @@ -.toolbar, -.infobar { - display: flex; - box-sizing: border-box; - flex-direction: row; - align-items: center; - white-space: nowrap; - padding: 0 16px; - width: 100%; - color: rgba(0, 0, 0, 0.87); - -} -.toolbar { - background: whitesmoke; - - .label { - margin: 0 !important; - max-width: 48%; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - } -} - -.clipboard-arkurl { - width: 264px; - height: 32px; - border: 1px solid rgba(0, 0, 0, 0.35); - border-radius: 4px 0 0 4px; -} -.ark-url-label { - margin-bottom: 8px -} -.btn-copy-arkurl { - border-radius: 0 4px 4px 0; - background-color: rgba(0, 0, 0, 0.35); -} diff --git a/src/app/workspace/resource/resource-toolbar/resource-toolbar.component.spec.ts b/src/app/workspace/resource/resource-toolbar/resource-toolbar.component.spec.ts deleted file mode 100644 index c75be75874..0000000000 --- a/src/app/workspace/resource/resource-toolbar/resource-toolbar.component.spec.ts +++ /dev/null @@ -1,182 +0,0 @@ -import { ClipboardModule } from '@angular/cdk/clipboard'; -import { Component, OnInit, ViewChild } from '@angular/core'; -import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing'; -import { FormsModule } from '@angular/forms'; -import { MatIconModule } from '@angular/material/icon'; -import { MatMenuModule } from '@angular/material/menu'; -import { MatSnackBarModule } from '@angular/material/snack-bar'; -import { MatTooltipModule } from '@angular/material/tooltip'; -import { By } from '@angular/platform-browser'; -import { - ApiResponseError, - MockProjects, - MockResource, - MockUsers, - ProjectsEndpointAdmin, - ReadResource -} from '@dasch-swiss/dsp-js'; -import { DspApiConnectionToken, UserService } from '@dasch-swiss/dsp-ui'; -import { of } from 'rxjs'; -import { ResourceToolbarComponent } from './resource-toolbar.component'; - -/** - * test host component to simulate parent component - */ -@Component({ - template: ` - - ` -}) -class TestResourceParentComponent implements OnInit { - - @ViewChild('resToolbar') resourceToolbarComponent: ResourceToolbarComponent; - - parentResource: ReadResource; - - showAllProps = true; - - constructor() { } - - ngOnInit() { - - MockResource.getTestThing().subscribe( - response => { - this.parentResource = response; - }, - (error: ApiResponseError) => { - console.error('Error to get the mock resource', error); - } - ); - } - - toggleProps(show: boolean) { - this.showAllProps = show; - } -} - -describe('ResourceToolbarComponent', () => { - let testHostComponent: TestResourceParentComponent; - let testHostFixture: ComponentFixture; - - beforeEach(waitForAsync(() => { - - const adminSpyObj = { - admin: { - projectsEndpoint: jasmine.createSpyObj('projectsEndpoint', ['getProjectByIri']) - } - }; - - const userServiceSpy = jasmine.createSpyObj('UserService', ['getUser']); - - TestBed.configureTestingModule({ - declarations: [ - ResourceToolbarComponent, - TestResourceParentComponent - ], - imports: [ - ClipboardModule, - MatIconModule, - MatMenuModule, - MatSnackBarModule, - MatTooltipModule, - FormsModule - ], - providers: [ - { - provide: DspApiConnectionToken, - useValue: adminSpyObj - }, - { - provide: UserService, - useValue: userServiceSpy - }, - ] - }) - .compileComponents(); - })); - - beforeEach(() => { - - const adminSpy = TestBed.inject(DspApiConnectionToken); - - // mock getProjectByIri response - (adminSpy.admin.projectsEndpoint as jasmine.SpyObj).getProjectByIri.and.callFake( - () => { - const project = MockProjects.mockProject(); - return of(project); - } - ); - - // mock getUserByIri response - const userSpy = TestBed.inject(UserService); - - // mock getUserByIri response - (userSpy as jasmine.SpyObj).getUser.and.callFake( - () => { - const user = MockUsers.mockUser(); - - return of(user.body); - } - ); - - testHostFixture = TestBed.createComponent(TestResourceParentComponent); - testHostComponent = testHostFixture.componentInstance; - testHostFixture.detectChanges(); - - expect(testHostComponent).toBeTruthy(); - }); - - it('should get the resource testding', () => { - - expect(testHostComponent.parentResource).toBeTruthy(); - expect(testHostComponent.parentResource.id).toEqual('http://rdfh.ch/0001/H6gBWUuJSuuO-CilHV8kQw'); - expect(testHostComponent.parentResource.label).toEqual('testding'); - - }); - - describe('Toolbar', () => { - let hostCompDe; - let propertyToolbarComponentDe; - - beforeEach(() => { - expect(testHostComponent.resourceToolbarComponent).toBeTruthy(); - - hostCompDe = testHostFixture.debugElement; - - propertyToolbarComponentDe = hostCompDe.query(By.directive(ResourceToolbarComponent)); - - expect(testHostComponent).toBeTruthy(); - - testHostFixture.detectChanges(); - }); - - it('should have the label "testding"', () => { - const resLabelDebugElement = propertyToolbarComponentDe.query(By.css('h3.label')); - const resLabelNativeElement = resLabelDebugElement.nativeElement; - - expect(resLabelNativeElement.textContent.trim()).toBe('testding'); - }); - - it('should toggle list of properties', () => { - const resLabelDebugElement = propertyToolbarComponentDe.query(By.css('button.toggle-props')); - const resLabelNativeElement = resLabelDebugElement.nativeElement; - // the button contains an icon "unfold_less" and the text "Decrease properties" - expect(resLabelNativeElement.textContent.trim()).toBe('unfold_lessHide empty properties'); - - resLabelNativeElement.click(); - - testHostFixture.detectChanges(); - - // the button contains an icon "unfold_more" and the text "Increase properties" - expect(resLabelNativeElement.textContent.trim()).toBe('unfold_moreShow all properties'); - - }); - }); - - - // tODO: currently not possible to test copy to clipboard from Material Angular - // https://stackoverflow.com/questions/60337742/test-copy-to-clipboard-function -}); diff --git a/src/app/workspace/resource/resource-toolbar/resource-toolbar.component.ts b/src/app/workspace/resource/resource-toolbar/resource-toolbar.component.ts deleted file mode 100644 index 8b7b925bdb..0000000000 --- a/src/app/workspace/resource/resource-toolbar/resource-toolbar.component.ts +++ /dev/null @@ -1,86 +0,0 @@ -import { Component, EventEmitter, Inject, Input, OnChanges, Output } from '@angular/core'; -import { MatSnackBar } from '@angular/material/snack-bar'; -import { ApiResponseData, ApiResponseError, KnoraApiConnection, ProjectResponse, ReadProject, ReadResource, ReadUser, UserResponse } from '@dasch-swiss/dsp-js'; -import { DspApiConnectionToken, NotificationService, UserService } from '@dasch-swiss/dsp-ui'; - -@Component({ - selector: 'app-resource-toolbar', - templateUrl: './resource-toolbar.component.html', - styleUrls: ['./resource-toolbar.component.scss'] -}) -export class ResourceToolbarComponent implements OnChanges { - - @Input() resource: ReadResource; - - @Input() showAllProps = false; - - @Output() toggleProps: EventEmitter = new EventEmitter(); - - /** - * output `referredProjectClicked` of resource view component: - * can be used to go to project page - */ - @Output() referredProjectClicked: EventEmitter = new EventEmitter(); - - /** - * output `referredProjectHovered` of resource view component: - * can be used for preview when hovering on project - */ - @Output() referredProjectHovered: EventEmitter = new EventEmitter(); - - project: ReadProject; - user: ReadUser; - - constructor( - @Inject(DspApiConnectionToken) private _dspApiConnection: KnoraApiConnection, - private _notification: NotificationService, - private _snackBar: MatSnackBar, - private _userService: UserService - ) { } - - ngOnChanges() { - // get project information - this._dspApiConnection.admin.projectsEndpoint.getProjectByIri(this.resource.attachedToProject).subscribe( - (response: ApiResponseData) => { - this.project = response.body.project; - }, - (error: ApiResponseError) => { - this._notification.openSnackBar(error); - } - ); - - // get user information - this._userService.getUser(this.resource.attachedToUser).subscribe( - (response: UserResponse) => { - this.user = response.user; - } - ); - } - - /** - * emits the project information on click. - */ - projectClicked(project: ReadProject) { - this.referredProjectClicked.emit(project); - } - - /** - * emits the project information on hover. - */ - projectHovered(project: ReadProject) { - this.referredProjectHovered.emit(project); - } - - /** - * display message to confirm the copy of the citation link (ARK URL) - */ - openSnackBar() { - const message = 'Copied to clipboard!'; - const action = 'Citation Link'; - this._snackBar.open(message, action, { - duration: 3000, - horizontalPosition: 'center', - verticalPosition: 'top' - }); - } -} From 3457d3992ee3f07d1fe632458f4f7dcf1267c9a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Kilchenmann?= Date: Wed, 26 May 2021 15:28:42 +0200 Subject: [PATCH 4/8] test(resource): enable all tests --- .../workspace/resource/properties/properties.component.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/workspace/resource/properties/properties.component.spec.ts b/src/app/workspace/resource/properties/properties.component.spec.ts index 05ceb12e81..502db0e208 100644 --- a/src/app/workspace/resource/properties/properties.component.spec.ts +++ b/src/app/workspace/resource/properties/properties.component.spec.ts @@ -132,7 +132,7 @@ class TestAddValueComponent { } -fdescribe('PropertiesComponent', () => { +describe('PropertiesComponent', () => { let testHostComponent: TestPropertyParentComponent; let testHostFixture: ComponentFixture; let voeService: ValueOperationEventService; From 0da8e692890292a641cc197e1740d9c63039d44b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Kilchenmann?= Date: Wed, 26 May 2021 15:33:08 +0200 Subject: [PATCH 5/8] refactor(resource): clean up code --- .../resource/resource.component.html | 50 ++----------------- 1 file changed, 4 insertions(+), 46 deletions(-) diff --git a/src/app/workspace/resource/resource.component.html b/src/app/workspace/resource/resource.component.html index 83488a1bd6..80a077f692 100644 --- a/src/app/workspace/resource/resource.component.html +++ b/src/app/workspace/resource/resource.component.html @@ -20,17 +20,6 @@ - - - @@ -38,28 +27,13 @@ [label]="incomingResource.res.entityInfo.classes[incomingResource.res.type].label"> - - - - - - - + - Annotations @@ -68,27 +42,11 @@ [class.active]="annotation.res.id === selectedRegion"> - - - - -
-
- -
+ From bbd792bdf54159fa57e3e233c4402d64c3f4afa1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Kilchenmann?= Date: Wed, 26 May 2021 15:46:22 +0200 Subject: [PATCH 6/8] refactor(resource): clean up code --- .../workspace/resource/properties/properties.component.scss | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/workspace/resource/properties/properties.component.scss b/src/app/workspace/resource/properties/properties.component.scss index 4adea67f06..19ab7298c5 100644 --- a/src/app/workspace/resource/properties/properties.component.scss +++ b/src/app/workspace/resource/properties/properties.component.scss @@ -1,3 +1,5 @@ +@import "../../../../assets/style/config"; + // toolbar .toolbar, .infobar { @@ -40,8 +42,6 @@ background-color: rgba(0, 0, 0, 0.35); } - - // // list of properties .border-bottom { From 9bdad1a2be28c07cb40d20d3c1427d96b35ed758 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Kilchenmann?= Date: Wed, 26 May 2021 15:52:57 +0200 Subject: [PATCH 7/8] style(resource): fix style issue in still image viewer --- .../representation/still-image/still-image.component.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/src/app/workspace/resource/representation/still-image/still-image.component.scss b/src/app/workspace/resource/representation/still-image/still-image.component.scss index 3129e07784..792bbacc74 100644 --- a/src/app/workspace/resource/representation/still-image/still-image.component.scss +++ b/src/app/workspace/resource/representation/still-image/still-image.component.scss @@ -21,6 +21,7 @@ $bright: #ccc; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; + max-width: 50%; } .action { From 17058ef20dce6576fcc4c6d81a18309d47b6558a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Kilchenmann?= Date: Wed, 26 May 2021 17:27:07 +0200 Subject: [PATCH 8/8] refactor: sort imports --- src/app/app.module.ts | 94 +++++++++++++++++++++---------------------- 1 file changed, 47 insertions(+), 47 deletions(-) diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 292b2f0186..1fcbd3fa85 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -21,77 +21,77 @@ import { AngularSplitModule } from 'angular-split'; import { environment } from '../environments/environment'; import { AppRoutingModule } from './app-routing.module'; import { AppComponent } from './app.component'; +import { AccountComponent } from './user/account/account.component'; +import { AddGroupComponent } from './project/permission/add-group/add-group.component'; +import { AddressTemplateComponent } from './project/board/address-template/address-template.component'; +import { AddUserComponent } from './project/collaboration/add-user/add-user.component'; +import { AttributionTabViewComponent } from './project/board/attribution-tab-view/attribution-tab-view.component'; +import { BoardComponent } from './project/board/board.component'; +import { CollaborationComponent } from './project/collaboration/collaboration.component'; +import { CollectionListComponent } from './user/collection-list/collection-list.component'; +import { ContactsTabViewComponent } from './project/board/contacts-tab-view/contacts-tab-view.component'; import { CookiePolicyComponent } from './main/cookie-policy/cookie-policy.component'; -import { DialogHeaderComponent } from './main/dialog/dialog-header/dialog-header.component'; +import { DashboardComponent } from './user/dashboard/dashboard.component'; +import { DatasetTabViewComponent } from './project/board/dataset-tab-view/dataset-tab-view.component'; import { DialogComponent } from './main/dialog/dialog.component'; -import { ExternalLinksDirective } from './main/directive/external-links.directive'; -import { InvalidControlScrollDirective } from './main/directive/invalid-control-scroll.directive'; +import { DialogHeaderComponent } from './main/dialog/dialog-header/dialog-header.component'; +import { EditListItemComponent } from './project/list/list-item-form/edit-list-item/edit-list-item.component'; import { ErrorComponent } from './main/error/error.component'; +import { ExternalLinksDirective } from './main/directive/external-links.directive'; import { FooterComponent } from './main/footer/footer.component'; import { GridComponent } from './main/grid/grid.component'; +import { GroupsComponent } from './system/groups/groups.component'; +import { GroupsListComponent } from './system/groups/groups-list/groups-list.component'; import { HeaderComponent } from './main/header/header.component'; import { HelpComponent } from './main/help/help.component'; +import { InvalidControlScrollDirective } from './main/directive/invalid-control-scroll.directive'; +import { ListComponent } from './project/list/list.component'; +import { ListInfoFormComponent } from './project/list/list-info-form/list-info-form.component'; +import { ListItemComponent } from './project/list/list-item/list-item.component'; +import { ListItemFormComponent } from './project/list/list-item-form/list-item-form.component'; import { LoginComponent } from './main/login/login.component'; import { MainComponent } from './main/main.component'; -import { SelectLanguageComponent } from './main/select-language/select-language.component'; import { MaterialModule } from './material-module'; -import { AddressTemplateComponent } from './project/board/address-template/address-template.component'; -import { AttributionTabViewComponent } from './project/board/attribution-tab-view/attribution-tab-view.component'; -import { BoardComponent } from './project/board/board.component'; -import { ContactsTabViewComponent } from './project/board/contacts-tab-view/contacts-tab-view.component'; -import { DatasetTabViewComponent } from './project/board/dataset-tab-view/dataset-tab-view.component'; +import { MembershipComponent } from './user/membership/membership.component'; +import { OntologyComponent } from './project/ontology/ontology.component'; +import { OntologyFormComponent } from './project/ontology/ontology-form/ontology-form.component'; +import { OntologyVisualizerComponent } from './project/ontology/ontology-visualizer/ontology-visualizer.component'; import { OrganisationTemplateComponent } from './project/board/organisation-template/organisation-template.component'; +import { PasswordFormComponent } from './user/user-form/password-form/password-form.component'; +import { PermissionComponent } from './project/permission/permission.component'; import { PersonTemplateComponent } from './project/board/person-template/person-template.component'; +import { ProfileComponent } from './user/profile/profile.component'; +import { ProjectComponent } from './project/project.component'; +import { ProjectFormComponent } from './project/project-form/project-form.component'; +import { ProjectsComponent } from './system/projects/projects.component'; +import { ProjectsListComponent } from './system/projects/projects-list/projects-list.component'; import { ProjectTabViewComponent } from './project/board/project-tab-view/project-tab-view.component'; -import { TermsTabViewComponent } from './project/board/terms-tab-view/terms-tab-view.component'; -import { UrlTemplateComponent } from './project/board/url-template/url-template.component'; -import { AddUserComponent } from './project/collaboration/add-user/add-user.component'; -import { CollaborationComponent } from './project/collaboration/collaboration.component'; -import { SelectGroupComponent } from './project/collaboration/select-group/select-group.component'; -import { ListInfoFormComponent } from './project/list/list-info-form/list-info-form.component'; -import { EditListItemComponent } from './project/list/list-item-form/edit-list-item/edit-list-item.component'; -import { ListItemFormComponent } from './project/list/list-item-form/list-item-form.component'; -import { ListItemComponent } from './project/list/list-item/list-item.component'; -import { ListComponent } from './project/list/list.component'; -import { OntologyFormComponent } from './project/ontology/ontology-form/ontology-form.component'; -import { OntologyVisualizerComponent } from './project/ontology/ontology-visualizer/ontology-visualizer.component'; -import { VisualizerComponent } from './project/ontology/ontology-visualizer/visualizer/visualizer.component'; -import { OntologyComponent } from './project/ontology/ontology.component'; +import { PropertiesComponent } from './workspace/resource/properties/properties.component'; import { PropertyFormComponent } from './project/ontology/property-form/property-form.component'; import { PropertyInfoComponent } from './project/ontology/property-info/property-info.component'; import { ResourceClassFormComponent } from './project/ontology/resource-class-form/resource-class-form.component'; import { ResourceClassInfoComponent } from './project/ontology/resource-class-info/resource-class-info.component'; import { ResourceClassPropertyFormComponent } from './project/ontology/resource-class-property-form/resource-class-property-form.component'; -import { AddGroupComponent } from './project/permission/add-group/add-group.component'; -import { PermissionComponent } from './project/permission/permission.component'; -import { ProjectFormComponent } from './project/project-form/project-form.component'; -import { ProjectComponent } from './project/project.component'; -import { GroupsListComponent } from './system/groups/groups-list/groups-list.component'; -import { GroupsComponent } from './system/groups/groups.component'; -import { ProjectsListComponent } from './system/projects/projects-list/projects-list.component'; -import { ProjectsComponent } from './system/projects/projects.component'; -import { SystemComponent } from './system/system.component'; -import { UsersListComponent } from './system/users/users-list/users-list.component'; -import { UsersComponent } from './system/users/users.component'; -import { AccountComponent } from './user/account/account.component'; -import { CollectionListComponent } from './user/collection-list/collection-list.component'; -import { DashboardComponent } from './user/dashboard/dashboard.component'; -import { MembershipComponent } from './user/membership/membership.component'; -import { ProfileComponent } from './user/profile/profile.component'; -import { PasswordFormComponent } from './user/user-form/password-form/password-form.component'; -import { UserFormComponent } from './user/user-form/user-form.component'; -import { UserMenuComponent } from './user/user-menu/user-menu.component'; -import { UserComponent } from './user/user.component'; +import { ResourceComponent } from './workspace/resource/resource.component'; import { ResourceInstanceFormComponent } from './workspace/resource/resource-instance-form/resource-instance-form.component'; +import { ResultsComponent } from './workspace/results/results.component'; +import { SelectGroupComponent } from './project/collaboration/select-group/select-group.component'; +import { SelectLanguageComponent } from './main/select-language/select-language.component'; import { SelectOntologyComponent } from './workspace/resource/resource-instance-form/select-ontology/select-ontology.component'; import { SelectProjectComponent } from './workspace/resource/resource-instance-form/select-project/select-project.component'; import { SelectPropertiesComponent } from './workspace/resource/resource-instance-form/select-properties/select-properties.component'; -import { SwitchPropertiesComponent } from './workspace/resource/resource-instance-form/select-properties/switch-properties/switch-properties.component'; import { SelectResourceClassComponent } from './workspace/resource/resource-instance-form/select-resource-class/select-resource-class.component'; -import { ResourceComponent } from './workspace/resource/resource.component'; -import { ResultsComponent } from './workspace/results/results.component'; import { StillImageComponent } from './workspace/resource/representation/still-image/still-image.component'; -import { PropertiesComponent } from './workspace/resource/properties/properties.component'; +import { SwitchPropertiesComponent } from './workspace/resource/resource-instance-form/select-properties/switch-properties/switch-properties.component'; +import { SystemComponent } from './system/system.component'; +import { TermsTabViewComponent } from './project/board/terms-tab-view/terms-tab-view.component'; +import { UrlTemplateComponent } from './project/board/url-template/url-template.component'; +import { UserComponent } from './user/user.component'; +import { UserFormComponent } from './user/user-form/user-form.component'; +import { UserMenuComponent } from './user/user-menu/user-menu.component'; +import { UsersComponent } from './system/users/users.component'; +import { UsersListComponent } from './system/users/users-list/users-list.component'; +import { VisualizerComponent } from './project/ontology/ontology-visualizer/visualizer/visualizer.component'; // translate: AoT requires an exported function for factories export function httpLoaderFactory(httpClient: HttpClient) {