Skip to content

Commit

Permalink
Merge branch 'main' into wip/DEV-2328-eu-remove-a-user-from-a-project…
Browse files Browse the repository at this point in the history
…-e2e
  • Loading branch information
irmastnt authored Feb 3, 2025
2 parents 9845ba7 + 1ce0477 commit 3029572
Show file tree
Hide file tree
Showing 26 changed files with 1,508 additions and 987 deletions.
46 changes: 46 additions & 0 deletions apps/dsp-app/cypress/e2e/system-admin/resource.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { Project00FFPayloads } from '../../fixtures/project00FF-resource-payload
import { ClassPropertyPayloads } from '../../fixtures/property-definition-payloads';
import { ResourceRequests, ResponseUtil } from '../../fixtures/requests';
import { AddResourceInstancePage } from '../../support/pages/add-resource-instance-page';
import { ResourcePage } from '../../support/pages/resource-page';

describe('Resource', () => {
let finalLastModificationDate: string;
Expand All @@ -22,6 +23,51 @@ describe('Resource', () => {
});
});

describe('footnotes', () => {
it.only('should be displayed, and can be edited', () => {
const footnote = {
'@type': 'http://0.0.0.0:3333/ontology/00FF/images/v2#datamodelclass',
'http://www.w3.org/2000/01/rdf-schema#label': 'rlabel',
'http://api.knora.org/ontology/knora-api/v2#attachedToProject': {
'@id': 'http://rdfh.ch/projects/00FF',
},
'http://0.0.0.0:3333/ontology/00FF/images/v2#property': {
'@type': 'http://api.knora.org/ontology/knora-api/v2#TextValue',
'http://api.knora.org/ontology/knora-api/v2#textValueAsXml':
'<?xml version="1.0" encoding="UTF-8"?><text><p>footnote1<footnote content="&amp;lt;p&amp;gt;fn1&amp;lt;/p&amp;gt;">[Footnote]</footnote> and footnote2<footnote content="&amp;lt;p&amp;gt;fn2&amp;lt;/p&amp;gt;">[Footnote]</footnote></p></text>',
'http://api.knora.org/ontology/knora-api/v2#textValueHasMapping': {
'@id': 'http://rdfh.ch/standoff/mappings/StandardMapping',
},
},
};

ResourceRequests.resourceRequest(ClassPropertyPayloads.richText(finalLastModificationDate));
cy.request('POST', `${Cypress.env('apiUrl')}/v2/resources`, footnote).then(v => {
const id = v.body['@id'].match(/\/([^\/]+)$/)[1];
const page = new ResourcePage();
page.visit(id);
cy.get('[data-cy=footnote]').should('have.length', 2);
cy.get('[data-cy=footnote]').eq(0).should('contain', 'fn1');
cy.get('[data-cy=footnote]').eq(1).should('contain', 'fn2');
});
cy.get('app-rich-text-switch').trigger('mouseenter');
cy.get('[data-cy="edit-button"]').click();
cy.get('[content="&lt;p&gt;fn1&lt;/p&gt;"]').click();
cy.get('.ck-content[contenteditable=true]')
.eq(1)
.then(el => {
// @ts-ignore
const editor = el[0].ckeditorInstance; // If you're using TS, this is ReturnType<typeof InlineEditor['create']>
editor.setData('Typing some stuff');
});
cy.get('.ck-button-save').click({ force: true });
cy.get('[data-cy="save-button"] > .mat-mdc-button-touch-target').click();
cy.get('[data-cy=footnote]').should('have.length', 2);
cy.get('[data-cy=footnote]').eq(0).should('contain', 'Typing some stuff');
cy.get('[data-cy=footnote]').eq(1).should('contain', 'fn2');
});
});

describe('can add an instance, edit, and delete for a property', () => {
it('text', () => {
const initialValue = faker.lorem.word();
Expand Down
18 changes: 18 additions & 0 deletions apps/dsp-app/cypress/fixtures/property-definition-payloads.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,24 @@ export class ClassPropertyPayloads {
};
}

static richText(lastModificationDate: string) {
return {
...this.baseData(lastModificationDate),
'@graph': [
{
...this.baseGraph,
...this.hasValue,
'http://api.knora.org/ontology/knora-api/v2#objectType': {
'@id': 'http://api.knora.org/ontology/knora-api/v2#TextValue',
},
'http://api.knora.org/ontology/salsah-gui/v2#guiElement': {
'@id': 'http://api.knora.org/ontology/salsah-gui/v2#Richtext',
},
},
],
};
}

static number(lastModificationDate: string) {
return {
...this.baseData(lastModificationDate),
Expand Down
5 changes: 5 additions & 0 deletions apps/dsp-app/cypress/support/pages/resource-page.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export class ResourcePage {
visit(id: string) {
cy.visit(`/project/00FF/ontology/00FF/images/${id}`);
}
}
4 changes: 2 additions & 2 deletions apps/dsp-app/src/assets/i18n/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,8 @@
"comment": "Kommentar",
"propertyLabels": {
"linkedProperty": "Verlinkte Ressource"
}
},
"footnotes": "Fussnoten"
},
"searchPanel": {
"howToSearch": "Wie suchen?",
Expand Down Expand Up @@ -222,7 +223,6 @@
"lastPage": "Letzte Seite",
"nextPage": "Nächste Seite",
"inputTooltip": "Seitennummer eingeben und über Enter zur gewählten Seite springen.",
"pageLabel": "Seite",
"previousPage": "Vorherige Seite",
"rangeLabelOf": "von",
"rangeLabelZero": "Seite 1 von 1"
Expand Down
4 changes: 2 additions & 2 deletions apps/dsp-app/src/assets/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,8 @@
"comment": "Comment",
"propertyLabels": {
"linkedProperty": "Linked resource"
}
},
"footnotes": "Footnotes"
},
"searchPanel": {
"howToSearch": "How to search",
Expand Down Expand Up @@ -249,7 +250,6 @@
"lastPage": "Last page",
"nextPage": "Next page",
"inputTooltip": "Enter the page number and apply with the Enter key.",
"pageLabel": "Page",
"previousPage": "Previous page",
"rangeLabelOf": "of",
"rangeLabelZero": "Page 1 of 1"
Expand Down
5 changes: 2 additions & 3 deletions apps/dsp-app/src/assets/i18n/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,8 @@
"comment": "Comment",
"propertyLabels": {
"linkedProperty": "Ressource liée"
}
},
"footnotes": "Notes"
},
"searchPanel": {
"howToSearch": "How to search",
Expand Down Expand Up @@ -250,10 +251,8 @@
"lastPage": "Dernière page",
"nextPage": "Page suivante",
"inputTooltip": "Saisir le numéro de page et appuyer sur Entrée pour aller à la page.",
"pageLabel": "Page",
"previousPage": "Page précédente",
"rangeLabelOf": "sur",
"rangeLabelZero": "Page 1 sur 1"
}

}
4 changes: 2 additions & 2 deletions apps/dsp-app/src/assets/i18n/it.json
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,8 @@
"comment": "Comment",
"propertyLabels": {
"linkedProperty": "Risorse collegate"
}
},
"footnotes": "Footnotes"
},
"searchPanel": {
"howToSearch": "How to search",
Expand Down Expand Up @@ -251,7 +252,6 @@
"nextPage": "Pagina successiva",
"inputTooltip": "Inserisci il numero della pagina e premi Invio per andare alla pagina.",
"previousPage": "Pagina precedente",
"pageLabel": "Pagina",
"rangeLabelOf": "di",
"rangeLabelZero": "Pagina 1 di 1"
}
Expand Down
4 changes: 2 additions & 2 deletions apps/dsp-app/src/assets/i18n/rm.json
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,8 @@
"comment": "Comment",
"propertyLabels": {
"linkedProperty": "Linked resource"
}
},
"footnotes": "Footnotes"
},
"searchPanel": {
"howToSearch": "How to search",
Expand Down Expand Up @@ -251,7 +252,6 @@
"nextPage": "Next page",
"inputTooltip": "Enter the page number and apply with the Enter key.",
"previousPage": "Previous page",
"pageLabel": "Page",
"rangeLabelOf": "of",
"rangeLabelZero": "Page 1 of 1"
}
Expand Down
8 changes: 5 additions & 3 deletions apps/dsp-app/src/styles.scss
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ $warn: mat.define-palette(config.$velvet_red_palette, 700, 500, 900);

// Define a theme.
$theme: mat.define-light-theme((
color: (primary: $primary, accent: $accent, warn: $warn),
density: 0
color: (primary: $primary, accent: $accent, warn: $warn),
density: 0
));

// Include all theme styles for the components.
Expand Down Expand Up @@ -67,7 +67,7 @@ body {
// The following styles will override material design!

// mat-form-field overrides
.mdc-text-field--filled:not(.mdc-text-field--disabled){
.mdc-text-field--filled:not(.mdc-text-field--disabled) {
background-color: transparent;
}

Expand Down Expand Up @@ -111,9 +111,11 @@ body {
.mat-mdc-text-field-wrapper {
width: 100%;
padding-left: 0px;

.mat-mdc-form-field-flex {
border-left: 1px solid rgba(0, 0, 0, 0.12);
min-height: calc(4 * 36px + 2px);

.mat-mdc-form-field-infix {
// negative values are not the best choice,
// but with this margin-top the placeholder is at the
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,13 +136,9 @@ mat-sidenav {
}

mat-sidenav-content {
.sidenav-expand-btn,
.main-content {
display: inline-block;
}

.main-content {
width: 95%;
display: block;
width: 100%;
}

.sidenav-expand-btn {
Expand Down
4 changes: 4 additions & 0 deletions libs/vre/resource-editor/resource-properties/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,7 @@ export * from './lib/sortByKeys';
export * from './lib/upload-control.component';
export * from './resource-properties.components';
export * from './lib/upload.component';
export * from './lib/footnote.service';
export * from './lib/footnotes.component';
export * from './lib/footnote-tooltip.component';
export * from './lib/footnote.directive';
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { animate, state, style, transition, trigger } from '@angular/animations';
import { Component, Input } from '@angular/core';
import { SafeHtml } from '@angular/platform-browser';

@Component({
selector: 'app-footnote-tooltip',
template: ` <div class="content" [@fadeIn]="'in'">
<div [innerHTML]="content"></div>
</div>`,
animations: [
trigger('fadeIn', [
state('in', style({ opacity: 1 })),
transition(':enter', [style({ opacity: 0 }), animate(100)]),
]),
],
styles: [
`
:host {
position: absolute;
z-index: 1000;
}
.content {
font-size: 0.8em;
background: white;
color: black;
padding: 8px;
min-width: 200px;
border-radius: 4px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
}
`,
],
})
export class FootnoteTooltipComponent {
@Input({ required: true }) content!: SafeHtml;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import { Overlay, OverlayPositionBuilder, OverlayRef } from '@angular/cdk/overlay';
import { ComponentPortal } from '@angular/cdk/portal';
import { Directive, HostListener } from '@angular/core';
import { DomSanitizer } from '@angular/platform-browser';
import { AppError } from '@dasch-swiss/vre/core/error-handler';
import { FootnoteTooltipComponent } from './footnote-tooltip.component';

@Directive({
selector: '[appFootnote]',
})
export class FootnoteDirective {
private _overlayRef: OverlayRef | null = null;
private _hideTimeout?: any;

constructor(
private _overlay: Overlay,
private _positionBuilder: OverlayPositionBuilder,
private _sanitizer: DomSanitizer
) {}

@HostListener('mouseover', ['$event'])
onMouseOver(event: MouseEvent) {
const targetElement = event.target as HTMLElement;
if (targetElement.nodeName.toLowerCase() === 'footnote') {
const content = targetElement.getAttribute('content');
if (content === null) {
throw new AppError('Footnote content is null');
}
this.showTooltip(content, event.clientX, event.clientY);
}
}

@HostListener('mouseout', ['$event.target'])
onMouseOut(targetElement: HTMLElement) {
if (targetElement.nodeName.toLowerCase() === 'footnote') {
this.hideTooltipWithDelay();
}
}

@HostListener('click', ['$event'])
onClick(event: MouseEvent): void {
const targetElement = event.target as HTMLElement;

if (targetElement.nodeName.toLowerCase() === 'footnote') {
const uuid = targetElement.getAttribute('id');

if (uuid) {
// Find the footnote with the same UUID and scroll to it
const targetFootnote = document.querySelector(`.footnote[data-uuid="${uuid}"]`);

if (targetFootnote) {
console.log('d', event);
// Scroll to the target footnote element
targetFootnote.scrollIntoView({ behavior: 'smooth', block: 'center' });
}
}
}
}

private showTooltip(content: string, mouseX: number, mouseY: number) {
if (this._overlayRef) {
this._overlayRef.detach();
}
const positionStrategy = this._positionBuilder.flexibleConnectedTo({ x: mouseX, y: mouseY }).withPositions([
{
overlayX: 'center',
overlayY: 'top',
originX: 'center',
originY: 'bottom',
offsetY: 10,
},
]);

this._overlayRef = this._overlay.create({
positionStrategy,
scrollStrategy: this._overlay.scrollStrategies.reposition(),
});

const tooltipPortal = new ComponentPortal(FootnoteTooltipComponent);
if (this._overlayRef.hasAttached()) {
this._overlayRef.detach();
clearTimeout(this._hideTimeout);
}

const tooltipRef = this._overlayRef.attach(tooltipPortal);
tooltipRef.instance.content = this._sanitizer.bypassSecurityTrustHtml(content);

this._overlayRef.overlayElement.addEventListener('mouseenter', () => {
clearTimeout(this._hideTimeout);
});

this._overlayRef.overlayElement.addEventListener('mouseleave', () => {
this.hideTooltipWithDelay();
});
}

private hideTooltipWithDelay() {
this._hideTimeout = setTimeout(() => {
this.hideTooltip();
}, 300);
}

private hideTooltip() {
if (this._overlayRef) {
this._overlayRef.detach();
}
}
}
Loading

0 comments on commit 3029572

Please sign in to comment.