From 49d9b7f2c1689c7fa7f572e09b47516253c05b60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Kilchenmann?= Date: Wed, 26 May 2021 22:48:39 +0200 Subject: [PATCH] refactor(resource): improve list of properties in resource viewer (#453) * feat(resource): combination of two components * test(resource): complete tests in properties comp * refactor(resource): delete unused components * test(resource): enable all tests * refactor(resource): clean up code * refactor(resource): clean up code * style(resource): fix style issue in still image viewer * refactor: sort imports --- src/app/app.module.ts | 100 ++++--- .../properties/properties.component.html | 126 +++++++++ .../properties.component.scss} | 66 ++++- .../properties.component.spec.ts} | 253 +++++++++++------- .../properties.component.ts} | 161 +++++++---- .../still-image/still-image.component.scss | 1 + .../resource-properties.component.html | 53 ---- .../resource-toolbar.component.html | 71 ----- .../resource-toolbar.component.scss | 37 --- .../resource-toolbar.component.spec.ts | 182 ------------- .../resource-toolbar.component.ts | 86 ------ .../resource/resource.component.html | 71 ++--- .../workspace/resource/resource.component.ts | 9 - 13 files changed, 512 insertions(+), 704 deletions(-) create mode 100644 src/app/workspace/resource/properties/properties.component.html rename src/app/workspace/resource/{resource-properties/resource-properties.component.scss => properties/properties.component.scss} (68%) rename src/app/workspace/resource/{resource-properties/resource-properties.component.spec.ts => properties/properties.component.spec.ts} (50%) rename src/app/workspace/resource/{resource-properties/resource-properties.component.ts => properties/properties.component.ts} (70%) delete mode 100644 src/app/workspace/resource/resource-properties/resource-properties.component.html 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 9cfd0e98af..1fcbd3fa85 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -21,78 +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 { 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 { 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) { @@ -146,6 +145,7 @@ export function httpLoaderFactory(httpClient: HttpClient) { ProjectsComponent, ProjectsListComponent, ProjectTabViewComponent, + PropertiesComponent, PropertyFormComponent, PropertyInfoComponent, ResourceClassFormComponent, @@ -160,6 +160,7 @@ export function httpLoaderFactory(httpClient: HttpClient) { SelectProjectComponent, SelectPropertiesComponent, SelectResourceClassComponent, + StillImageComponent, SwitchPropertiesComponent, SystemComponent, TermsTabViewComponent, @@ -170,9 +171,6 @@ export function httpLoaderFactory(httpClient: HttpClient) { UsersComponent, UsersListComponent, VisualizerComponent, - ResourceToolbarComponent, - ResourcePropertiesComponent, - StillImageComponent, ], 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/resource-properties/resource-properties.component.scss b/src/app/workspace/resource/properties/properties.component.scss similarity index 68% rename from src/app/workspace/resource/resource-properties/resource-properties.component.scss rename to src/app/workspace/resource/properties/properties.component.scss index 0de3b07816..19ab7298c5 100644 --- a/src/app/workspace/resource/resource-properties/resource-properties.component.scss +++ b/src/app/workspace/resource/properties/properties.component.scss @@ -1,27 +1,73 @@ +@import "../../../../assets/style/config"; + +// 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,.1); + 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; + cursor: pointer; + border: none; + padding: 0em; + outline: none; + background-color: transparent; + color: #000000; } .info .material-icons, .create .material-icons { - font-size: 18px; + font-size: 18px; } .info .mat-icon, .create .mat-icon { - width: 18px; - height: 18px; + width: 18px; + height: 18px; } // properties container with property item and property value items diff --git a/src/app/workspace/resource/resource-properties/resource-properties.component.spec.ts b/src/app/workspace/resource/properties/properties.component.spec.ts similarity index 50% rename from src/app/workspace/resource/resource-properties/resource-properties.component.spec.ts rename to src/app/workspace/resource/properties/properties.component.spec.ts index 70c4e67127..502db0e208 100644 --- a/src/app/workspace/resource/resource-properties/resource-properties.component.spec.ts +++ b/src/app/workspace/resource/properties/properties.component.spec.ts @@ -1,6 +1,9 @@ +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'; @@ -9,7 +12,10 @@ import { Constants, IHasPropertyWithPropertyDefinition, KnoraApiConnection, + MockProjects, MockResource, + MockUsers, + ProjectsEndpointAdmin, ReadLinkValue, ReadResource, ReadValue, @@ -24,46 +30,33 @@ import { EmitEvent, Events, PropertyInfoValues, + UserService, ValueOperationEventService } from '@dasch-swiss/dsp-ui'; -import { Subscription } from 'rxjs'; +import { of, Subscription } from 'rxjs'; import { TestConfig } from 'test.config'; -import { ResourcePropertiesComponent } from './resource-properties.component'; +import { DspResource } from '../dsp-resource'; +import { PropertiesComponent } from './properties.component'; /** * test host component to simulate parent component. */ @Component({ template: ` - - ` + + ` }) class TestPropertyParentComponent implements OnInit, OnDestroy { - @ViewChild('propView') resourcePropertiesComponent: ResourcePropertiesComponent; + @ViewChild('propView') propertiesComponent: PropertiesComponent; - parentResource: ReadResource; - - propArray: PropertyInfoValues[] = []; - - systemPropArray: SystemPropertyDefinition[] = []; - - showAllProps = false; + parentResource: DspResource; voeSubscriptions: Subscription[] = []; myNum = 0; - linkValClicked: ReadLinkValue; - - linkValHovered: ReadLinkValue; - constructor(public _valueOperationEventService: ValueOperationEventService) { } ngOnInit() { @@ -73,25 +66,25 @@ class TestPropertyParentComponent implements OnInit, OnDestroy { MockResource.getTestThing().subscribe( (response: ReadResource) => { - this.parentResource = response; + this.parentResource = new DspResource(response); // gather resource property information - this.propArray = this.parentResource.entityInfo.classes[this.parentResource.type].getResourcePropertiesList().map( + 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.getValues(prop.propertyIndex) + values: this.parentResource.res.getValues(prop.propertyIndex) }; return propInfoAndValues; } ); // sort properties by guiOrder - this.propArray.sort((a, b) => (a.guiDef.guiOrder > b.guiDef.guiOrder) ? 1 : -1); + this.parentResource.resProps.sort((a, b) => (a.guiDef.guiOrder > b.guiDef.guiOrder) ? 1 : -1); // get system property information - this.systemPropArray = this.parentResource.entityInfo.getPropertyDefinitionsByType(SystemPropertyDefinition); + this.parentResource.systemProps = this.parentResource.res.entityInfo.getPropertyDefinitionsByType(SystemPropertyDefinition); }, (error: ApiResponseError) => { @@ -105,14 +98,6 @@ class TestPropertyParentComponent implements OnInit, OnDestroy { this.voeSubscriptions.forEach(sub => sub.unsubscribe()); } } - - internalLinkClicked(linkVal: ReadLinkValue) { - this.linkValClicked = linkVal; - } - - internalLinkHovered(linkVal: ReadLinkValue) { - this.linkValHovered = linkVal; - } } /** @@ -124,9 +109,8 @@ class TestPropertyParentComponent implements OnInit, OnDestroy { }) class TestDisplayValueComponent { - @Input() parentResource: ReadResource; + @Input() parentResource: DspResource; @Input() displayValue: ReadValue; - @Input() propArray: PropertyInfoValues[]; @Input() configuration?: object; @Output() referredResourceClicked: EventEmitter = new EventEmitter(); @@ -143,21 +127,33 @@ class TestDisplayValueComponent { }) class TestAddValueComponent { - @Input() parentResource: ReadResource; + @Input() parentResource: DspResource; @Input() resourcePropertyDefinition: ResourcePropertyDefinition; } -describe('ResourcePropertiesComponent', () => { +describe('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 ], @@ -165,7 +161,7 @@ describe('ResourcePropertiesComponent', () => { TestPropertyParentComponent, TestDisplayValueComponent, TestAddValueComponent, - ResourcePropertiesComponent + PropertiesComponent ], providers: [ ValueOperationEventService, @@ -177,7 +173,15 @@ describe('ResourcePropertiesComponent', () => { { provide: DspApiConnectionToken, useValue: new KnoraApiConnection(TestConfig.ApiConfig) - } + }, + { + provide: DspApiConnectionToken, + useValue: adminSpyObj + }, + { + provide: UserService, + useValue: userServiceSpy + }, ] }) .compileComponents(); @@ -186,6 +190,28 @@ describe('ResourcePropertiesComponent', () => { })); 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(); @@ -193,90 +219,111 @@ describe('ResourcePropertiesComponent', () => { expect(testHostComponent).toBeTruthy(); }); + it('should get the resource testding', () => { - it('should get 25 properties', () => { - - expect(testHostComponent.propArray).toBeTruthy(); - expect(testHostComponent.propArray.length).toBe(25); + expect(testHostComponent.parentResource).toBeTruthy(); + expect(testHostComponent.parentResource.res.id).toEqual('http://rdfh.ch/0001/H6gBWUuJSuuO-CilHV8kQw'); + expect(testHostComponent.parentResource.res.label).toEqual('testding'); }); - it('should get the resource testding', () => { + describe('Toolbar', () => { + let hostCompDe; + let propertyToolbarComponentDe; - expect(testHostComponent.parentResource).toBeTruthy(); - expect(testHostComponent.parentResource.id).toEqual('http://rdfh.ch/0001/H6gBWUuJSuuO-CilHV8kQw'); - expect(testHostComponent.parentResource.label).toEqual('testding'); + beforeEach(() => { + expect(testHostComponent.propertiesComponent).toBeTruthy(); - }); + hostCompDe = testHostFixture.debugElement; - it('should display a text value among the property list', () => { + propertyToolbarComponentDe = hostCompDe.query(By.directive(PropertiesComponent)); - 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'); + expect(testHostComponent).toBeTruthy(); - }); + testHostFixture.detectChanges(); + }); - it('should get some system properties', () => { + it('should have the label "testding"', () => { + const resLabelDebugElement = propertyToolbarComponentDe.query(By.css('h3.label')); + const resLabelNativeElement = resLabelDebugElement.nativeElement; - expect(testHostComponent.systemPropArray).toBeTruthy(); - expect(testHostComponent.systemPropArray.length).toEqual(13); + expect(resLabelNativeElement.textContent.trim()).toBe('testding'); + }); - // check if the first system property is an ARK url - expect(testHostComponent.systemPropArray[0].label).toEqual('ARK URL'); + 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(); - it('should propagate a click event on a link value', () => { + testHostFixture.detectChanges(); - const displayEdit = testHostFixture.debugElement.query(By.directive(TestDisplayValueComponent)); + // the button contains an icon "unfold_less" and the text "Decrease properties" + expect(resLabelNativeElement.textContent.trim()).toBe('unfold_lessHide empty properties'); - const linkVal = new ReadLinkValue(); - linkVal.linkedResourceIri = 'testIri'; + }); + }); - expect(testHostComponent.linkValClicked).toBeUndefined(); + // --> TODO: currently not possible to test copy to clipboard from Material Angular + // https://stackoverflow.com/questions/60337742/test-copy-to-clipboard-function - (displayEdit.componentInstance as TestDisplayValueComponent).referredResourceClicked.emit(linkVal); + describe('List of properties', () => { + it('should get 25 properties', () => { - expect(testHostComponent.linkValClicked.linkedResourceIri).toEqual('testIri'); + 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 propagate a hover event on a link value', () => { + }); - const displayEdit = testHostFixture.debugElement.query(By.directive(TestDisplayValueComponent)); + it('should display a text value among the property list', () => { - const linkVal = new ReadLinkValue(); - linkVal.linkedResourceIri = 'testIri'; + 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'); - expect(testHostComponent.linkValHovered).toBeUndefined(); + }); - (displayEdit.componentInstance as TestDisplayValueComponent).referredResourceHovered.emit(linkVal); + it('should get some system properties', () => { - expect(testHostComponent.linkValHovered.linkedResourceIri).toEqual('testIri'); + 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); + it('should trigger the callback when an event is emitted', () => { - voeService.emit(new EmitEvent(Events.ValueAdded)); + expect(testHostComponent.myNum).toEqual(0); - expect(testHostComponent.myNum).toEqual(1); - }); + voeService.emit(new EmitEvent(Events.ValueAdded)); - it('should unsubscribe from changes when destroyed', () => { - testHostComponent.voeSubscriptions.forEach(sub => { - expect(sub.closed).toBe(false); + expect(testHostComponent.myNum).toEqual(1); }); - testHostFixture.destroy(); + 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); + testHostComponent.voeSubscriptions.forEach(sub => { + expect(sub.closed).toBe(true); + }); }); }); @@ -285,16 +332,16 @@ describe('ResourcePropertiesComponent', () => { let propertyViewComponentDe; beforeEach(() => { - expect(testHostComponent.resourcePropertiesComponent).toBeTruthy(); + expect(testHostComponent.propertiesComponent).toBeTruthy(); hostCompDe = testHostFixture.debugElement; - propertyViewComponentDe = hostCompDe.query(By.directive(ResourcePropertiesComponent)); + propertyViewComponentDe = hostCompDe.query(By.directive(PropertiesComponent)); expect(testHostComponent).toBeTruthy(); - testHostComponent.resourcePropertiesComponent.addButtonIsVisible = true; - testHostComponent.resourcePropertiesComponent.addValueFormIsVisible = false; + testHostComponent.propertiesComponent.addButtonIsVisible = true; + testHostComponent.propertiesComponent.addValueFormIsVisible = false; testHostFixture.detectChanges(); }); @@ -307,7 +354,9 @@ describe('ResourcePropertiesComponent', () => { 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; + const toggleAllPropsElement = testHostFixture.debugElement.query(By.css('button.toggle-props')); + toggleAllPropsElement.nativeElement.click(); + testHostFixture.detectChanges(); let addButtons = propertyViewComponentDe.queryAll(By.css('button.create')); @@ -318,7 +367,7 @@ describe('ResourcePropertiesComponent', () => { expect(addButtons.length).toEqual(17); // remove value from the boolean property - testHostComponent.propArray[9].values = []; + testHostComponent.parentResource.resProps[9].values = []; testHostFixture.detectChanges(); @@ -351,31 +400,31 @@ describe('ResourcePropertiesComponent', () => { it('should determine that adding a standoff link value is not allowed', () => { - const standoffLinkVal = testHostComponent.propArray.filter( + const standoffLinkVal = testHostComponent.parentResource.resProps.filter( propVal => propVal.propDef.id === Constants.HasStandoffLinkToValue ); - expect(testHostComponent.resourcePropertiesComponent.addValueIsAllowed(standoffLinkVal[0])).toBeFalsy(); + expect(testHostComponent.propertiesComponent.addValueIsAllowed(standoffLinkVal[0])).toBeFalsy(); }); it('should determine that adding a incoming link value is not allowed', () => { - const standoffLinkVal = testHostComponent.propArray.filter( + const standoffLinkVal = testHostComponent.parentResource.resProps.filter( propVal => propVal.propDef.id === 'http://api.knora.org/ontology/knora-api/v2#hasIncomingLinkValue' ); - expect(testHostComponent.resourcePropertiesComponent.addValueIsAllowed(standoffLinkVal[0])).toBeFalsy(); + expect(testHostComponent.propertiesComponent.addValueIsAllowed(standoffLinkVal[0])).toBeFalsy(); }); it('should determine that adding an int value is allowed', () => { - const standoffLinkVal = testHostComponent.propArray.filter( + const standoffLinkVal = testHostComponent.parentResource.resProps.filter( propVal => propVal.propDef.id === 'http://0.0.0.0:3333/ontology/0001/anything/v2#hasInteger' ); - expect(testHostComponent.resourcePropertiesComponent.addValueIsAllowed(standoffLinkVal[0])).toBeTruthy(); + expect(testHostComponent.propertiesComponent.addValueIsAllowed(standoffLinkVal[0])).toBeTruthy(); }); diff --git a/src/app/workspace/resource/resource-properties/resource-properties.component.ts b/src/app/workspace/resource/properties/properties.component.ts similarity index 70% rename from src/app/workspace/resource/resource-properties/resource-properties.component.ts rename to src/app/workspace/resource/properties/properties.component.ts index 57b5299686..135fdbf7a0 100644 --- a/src/app/workspace/resource/resource-properties/resource-properties.component.ts +++ b/src/app/workspace/resource/properties/properties.component.ts @@ -1,79 +1,79 @@ +import { Component, EventEmitter, Inject, Input, OnChanges, OnDestroy, OnInit, Output } from '@angular/core'; +import { MatSnackBar } from '@angular/material/snack-bar'; import { - Component, EventEmitter, - Inject, - Input, - OnDestroy, - OnInit, - Output, - ViewChild -} from '@angular/core'; -import { + ApiResponseData, + ApiResponseError, CardinalityUtil, Constants, DeleteValue, KnoraApiConnection, PermissionUtil, + ProjectResponse, ReadLinkValue, - ReadResource, + ReadProject, ReadResourceSequence, ReadTextValueAsXml, + ReadUser, ReadValue, ResourcePropertyDefinition, - SystemPropertyDefinition + UserResponse } from '@dasch-swiss/dsp-js'; import { AddedEventValue, - AddValueComponent, DeletedEventValue, - DisplayEditComponent, 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-resource-properties', - templateUrl: './resource-properties.component.html', - styleUrls: ['./resource-properties.component.scss'] + selector: 'app-properties', + templateUrl: './properties.component.html', + styleUrls: ['./properties.component.scss'] }) -export class ResourcePropertiesComponent implements OnInit, OnDestroy { +export class PropertiesComponent implements OnInit, OnChanges, OnDestroy { - @ViewChild('displayEdit') displayEditComponent: DisplayEditComponent; - @ViewChild('addValue') addValueComponent: AddValueComponent; /** - * parent resource - * - * @param (parentResource) + * input `resource` of properties component: + * complete information about the current resource */ - @Input() parentResource: ReadResource; + @Input() resource: DspResource; /** - * array of property object with ontology class prop def, list of properties and corresponding values - * - * @param (propArray) + * input `displayProjectInfo` of properties component: + * display project info or not; "This resource belongs to project XYZ" */ - @Input() propArray: PropertyInfoValues[]; + @Input() displayProjectInfo: false; /** - * array of system property object with list of system properties - * - * @param (systemPropArray) + * output `referredProjectClicked` of resource view component: + * can be used to go to project page */ - @Input() systemPropArray: SystemPropertyDefinition[]; + @Output() referredProjectClicked: EventEmitter = new EventEmitter(); /** - * show all properties, even if they don't have a value. - * - * @param (showAllProps) + * output `referredProjectHovered` of resource view component: + * can be used for preview when hovering on project */ - @Input() showAllProps = false; + @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 @@ -82,17 +82,25 @@ export class ResourcePropertiesComponent implements OnInit, OnDestroy { 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() { - if (this.parentResource) { + ngOnInit(): void { + if (this.resource.res) { // get user permissions const allPermissions = PermissionUtil.allUserPermissions( - this.parentResource.userHasPermission as 'RV' | 'V' | 'M' | 'D' | 'CR' + 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 @@ -123,6 +131,25 @@ export class ResourcePropertiesComponent implements OnInit, OnDestroy { )); } + 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) { @@ -134,6 +161,43 @@ export class ResourcePropertiesComponent implements OnInit, OnDestroy { } } + /** + * 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 */ @@ -165,7 +229,7 @@ export class ResourcePropertiesComponent implements OnInit, OnDestroy { } const isAllowed = CardinalityUtil.createValueForPropertyAllowed( - prop.propDef.id, prop.values.length, this.parentResource.entityInfo.classes[this.parentResource.type]); + 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 @@ -180,8 +244,8 @@ export class ResourcePropertiesComponent implements OnInit, OnDestroy { * @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 + if (this.resource.resProps) { + this.resource.resProps .filter(propInfoValueArray => propInfoValueArray.propDef.id === valueToAdd.property) // filter to the correct property .forEach(propInfoValue => @@ -190,6 +254,7 @@ export class ResourcePropertiesComponent implements OnInit, OnDestroy { this._updateStandoffLinkValue(); } } else { + // --> TODO: better error handler! console.error('No properties exist for this resource'); } } @@ -201,8 +266,8 @@ export class ResourcePropertiesComponent implements OnInit, OnDestroy { * @param updatedValue the value to replace valueToReplace with */ updateValueInResource(valueToReplace: ReadValue, updatedValue: ReadValue): void { - if (this.propArray && updatedValue !== null) { - this.propArray + if (this.resource.resProps && updatedValue !== null) { + this.resource.resProps .filter(propInfoValueArray => propInfoValueArray.propDef.id === valueToReplace.property) // filter to the correct property .forEach(filteredpropInfoValueArray => { @@ -226,8 +291,8 @@ export class ResourcePropertiesComponent implements OnInit, OnDestroy { * @param valueToDelete the value to remove from the values array of the filtered property */ deleteValueFromResource(valueToDelete: DeleteValue): void { - if (this.propArray) { - this.propArray + if (this.resource.resProps) { + this.resource.resProps .filter(propInfoValueArray => // filter to the correct type this._valueService.compareObjectTypeWithValueType(propInfoValueArray.propDef.objectType, valueToDelete.type)) .forEach(filteredpropInfoValueArray => { @@ -252,7 +317,7 @@ export class ResourcePropertiesComponent implements OnInit, OnDestroy { */ private _updateStandoffLinkValue(): void { - if (this.parentResource === undefined) { + 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. @@ -265,7 +330,7 @@ export class ResourcePropertiesComponent implements OnInit, OnDestroy { ?res knora-api:isMainResource true . ?res knora-api:hasStandoffLinkTo ?target . } WHERE { - BIND(<${this.parentResource.id}> as ?res) . + BIND(<${this.resource.res.id}> as ?res) . OPTIONAL { ?res knora-api:hasStandoffLinkTo ?target . } @@ -283,7 +348,7 @@ export class ResourcePropertiesComponent implements OnInit, OnDestroy { const newStandoffLinkVals = res.resources[0].getValuesAs(Constants.HasStandoffLinkToValue, ReadLinkValue); - this.propArray.filter( + this.resource.resProps.filter( resPropInfoVal => (resPropInfoVal.propDef.id === Constants.HasStandoffLinkToValue) ).forEach( standoffLinkResPropInfoVal => { 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 { 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-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' - }); - } -} diff --git a/src/app/workspace/resource/resource.component.html b/src/app/workspace/resource/resource.component.html index 3f3ad2cdff..80a077f692 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,74 +18,35 @@ - - - - - - - - - - The resource {{resource?.res.resourceClassLabel}} has no defined - properties. + + - - - - - - - - - - - The resource {{resource?.res.resourceClassLabel}} has no defined properties. + + - + - + Annotations -
- - - - - - - - The resource {{resource?.res.resourceClassLabel}} has no defined properties. +
+ +
- - -
+
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;