Skip to content

Commit

Permalink
Merge branch 'development' into lukaskabc/Enhancement-520
Browse files Browse the repository at this point in the history
  • Loading branch information
lukaskabc authored Nov 24, 2024
2 parents fe71d6c + 2f593d1 commit faba150
Show file tree
Hide file tree
Showing 27 changed files with 363 additions and 147 deletions.
6 changes: 6 additions & 0 deletions NEWS.cs.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
#### Verze 3.3.0

- Přidána možnost stáhnout anotovaný soubor bez nepotvrzených výskytů.
- Upraveno fulltextové vyhledávání - nově se zobrazuje informace o atributu, ve kterém byla shoda nalezena.
- Do fasetového vyhledávání přidána možnost filtrovat dle příkladů (`skos:example`).

#### Verze 3.2.0

- Přidána podpora pro import slovníků z MS Excel.
Expand Down
6 changes: 6 additions & 0 deletions NEWS.en.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
#### Version 3.3.0

- Added the possibility to download annotated file without unconfirmed occurrences.
- Enhanced fulltext search - now it shows information about the attribute in which a match was found.
- Added support for filtering by example (`skos:example`) in the faceted search.

#### Version 3.2.0

- Added support for importing a vocabulary from MS Excel.
Expand Down
18 changes: 9 additions & 9 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "termit-ui",
"version": "3.2.0",
"version": "3.3.1",
"private": true,
"homepage": ".",
"license": "GPL-3.0-only",
Expand Down Expand Up @@ -83,7 +83,7 @@
"@redux-devtools/extension": "^3.2.5",
"@types/enzyme": "^3.10.13",
"@types/jest": "^27.4.1",
"@types/js-cookie": "^3.0.3",
"@types/js-cookie": "^3.0.6",
"@types/lodash": "^4.17.10",
"@types/luxon": "^3.4.2",
"@types/node": "^18.11.17",
Expand Down
2 changes: 2 additions & 0 deletions src/component/annotator/Annotation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ interface AnnotationProps extends AnnotationSpanProps {
accessLevel: AccessLevel;
highlight?: boolean;
filter: AnnotatorLegendFilter;
language?: string;
}

interface AnnotationState {
Expand Down Expand Up @@ -316,6 +317,7 @@ export class Annotation extends React.Component<
onToggleDetailOpen={this.toggleOpenDetail}
onClose={this.onCloseDetail}
accessLevel={this.props.accessLevel}
language={this.props.language}
/>
);
}
Expand Down
6 changes: 5 additions & 1 deletion src/component/annotator/Annotator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ interface AnnotatorProps extends HasI18n {
user: User;
file: File;
vocabulary: Vocabulary;
annotationLanguage?: string;

onUpdate: (newHtml: string) => void;
setAnnotatorLegendFilter: (
Expand Down Expand Up @@ -625,7 +626,7 @@ export class Annotator extends React.Component<AnnotatorProps, AnnotatorState> {
<HeaderWithActions
title={this.renderTitle()}
className={classNames("annotator-header", {
"annotator-header-scrolled": window.pageYOffset > 0,
"annotator-header-scrolled": window.scrollY > 0,
})}
actions={[
<HighlightTermOccurrencesButton
Expand Down Expand Up @@ -708,6 +709,9 @@ export class Annotator extends React.Component<AnnotatorProps, AnnotatorState> {
onRemove={this.onRemove}
onResetSticky={this.resetStickyAnnotationId}
highlightedTerm={this.state.highlightedTerm}
annotationLanguage={
this.props.annotationLanguage || this.props.file.language
}
/>
</div>
</CardBody>
Expand Down
16 changes: 14 additions & 2 deletions src/component/annotator/AnnotatorContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ interface AnnotatorContentProps {
content: DomHandlerNode[];
accessLevel: AccessLevel; // The level of access rights the current user has
highlightedTerm: TermData | null;
annotationLanguage?: string;

onRemove: (annotationId: string | string[]) => void;
onUpdate: (annotationSpan: AnnotationSpanProps, term: Term | null) => void;
Expand All @@ -35,8 +36,16 @@ const PREPROCESSING_INSTRUCTIONS = [
shouldPreprocessNode: (node: any): boolean =>
node.name && node.name === "a",
preprocessNode: (node: any) => {
node.attribs["data-href"] = node.attribs.href;
delete node.attribs.href;
// Remove href from relative links, absolute links will open in blank tab
if (!Utils.isLink(node.attribs.href)) {
node.attribs["data-href"] = node.attribs.href;
delete node.attribs.href;
} else {
node.attribs["data-target"] = node.attribs.target;
node.attribs["target"] = "_blank";
node.attribs["data-rel"] = node.attribs.rel;
node.attribs["rel"] = "noopener noreferrer";
}
},
},
];
Expand All @@ -56,6 +65,7 @@ const AnnotatorContent: React.FC<AnnotatorContentProps> = (props) => {
onCreateTerm,
accessLevel,
highlightedTerm,
annotationLanguage,
} = props;

// Using memoization to skip processing and re-rendering of the content DOM in case it hasn't changed
Expand Down Expand Up @@ -104,6 +114,7 @@ const AnnotatorContent: React.FC<AnnotatorContentProps> = (props) => {
highlightedTerm !== null &&
elem.attribs.resource === highlightedTerm.iri
}
language={annotationLanguage}
{...attribs}
>
{children}
Expand Down Expand Up @@ -134,6 +145,7 @@ const AnnotatorContent: React.FC<AnnotatorContentProps> = (props) => {
onCreateTerm,
accessLevel,
highlightedTerm,
annotationLanguage,
]);

return (
Expand Down
23 changes: 22 additions & 1 deletion src/component/annotator/HtmlParserUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,33 @@ import render from "dom-serializer";

const RDF_ATTRIBUTE_NAMES = ["about", "property", "resource", "typeof"];

function removeAddedAttributes(elem: Element) {
// Restore links to their original state - see AnnotatorContent.PREPROCESSING_INSTRUCTIONS
if (elem.tagName === "a") {
if (elem.attribs["data-href"]) {
elem.attribs.href = elem.attribs["data-href"];
delete elem.attribs["data-href"];
}
delete elem.attribs["target"];
delete elem.attribs["rel"];
if (elem.attribs["data-rel"]) {
elem.attribs.rel = elem.attribs["data-rel"];
delete elem.attribs["data-rel"];
}
if (elem.attribs["data-target"]) {
elem.attribs.target = elem.attribs["data-target"];
delete elem.attribs["data-target"];
}
}
}

const HtmlParserUtils = {
html2dom(html: string): Node[] {
// Do not decode HTML entities (e.g., &lt;) when parsing content for object representation, it caused issues
// with rendering
const options = { decodeEntities: false };
const handler = new DomHandler();
// @ts-ignore
const handler = new DomHandler(null, null, removeAddedAttributes);
const parser = new HtmlParser(handler, options);
parser.parseComplete(html);
return handler.dom as Node[];
Expand Down
2 changes: 2 additions & 0 deletions src/component/annotator/TermDefinitionAnnotation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ interface TermDefinitionAnnotationProps {
text: string;
isOpen: boolean;
accessLevel: AccessLevel;
language?: string;

onRemove: () => void;
onSelectTerm: (term: Term | null) => void;
Expand Down Expand Up @@ -118,6 +119,7 @@ export const TermDefinitionAnnotation: React.FC<
term={term}
resource={props.resource}
textContent={props.text}
language={props.language}
/>
);

Expand Down
3 changes: 2 additions & 1 deletion src/component/annotator/TermDefinitionAnnotationView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ interface TermDefinitionAnnotationViewProps {
term?: Term | null;
resource?: string;
textContent: string;
language?: string;
}

const TermDefinitionAnnotationView: React.FC<
Expand All @@ -20,7 +21,7 @@ const TermDefinitionAnnotationView: React.FC<
<tr>
<td className="label">{i18n("annotation.definition.term")}</td>
<td>
<TermLink term={props.term} />
<TermLink term={props.term} language={props.language} />
</td>
</tr>
</tbody>
Expand Down
2 changes: 2 additions & 0 deletions src/component/annotator/TermOccurrenceAnnotation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ interface TermOccurrenceAnnotationProps {
annotationOrigin: string;
isOpen: boolean;
accessLevel: AccessLevel;
language?: string;

onRemove: () => void;
onSelectTerm: (term: Term | null) => void;
Expand Down Expand Up @@ -129,6 +130,7 @@ export const TermOccurrenceAnnotation: React.FC<
score={props.score}
resource={props.resource}
annotationClass={props.annotationClass}
language={props.language}
/>
);

Expand Down
3 changes: 2 additions & 1 deletion src/component/annotator/TermOccurrenceAnnotationView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ interface TermOccurrenceAnnotationViewProps {
score?: string;
resource?: string;
annotationClass: string;
language?: string;
}

const TermOccurrenceAnnotationView: React.FC<
Expand All @@ -25,7 +26,7 @@ const TermOccurrenceAnnotationView: React.FC<
{i18n("annotation.term.assigned-occurrence.termLabel")}
</td>
<td>
<TermLink term={props.term!} />
<TermLink term={props.term!} language={props.language} />
</td>
</tr>
</tbody>
Expand Down
58 changes: 56 additions & 2 deletions src/component/annotator/__tests__/Annotator.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ describe("Annotator", () => {
expect(wrapper.html().includes(sampleContent)).toBe(true);
});

it("renders body of provided html content with replaced anchor hrefs", () => {
it("preserves absolute URL href anchors", () => {
const htmlContent = surroundWithHtml(
'This is a <a href="https://example.org/link">link</a>'
);
Expand All @@ -138,7 +138,28 @@ describe("Annotator", () => {
)
);
const sampleOutput =
'This is a <a data-href="https://example.org/link">link</a>';
'This is a <a href="https://example.org/link" target="_blank" rel="noopener noreferrer">link</a>';
expect(wrapper.html().includes(sampleOutput)).toBe(true);
});

it("renders body of provided html content with replaced relative anchor hrefs", () => {
const htmlContent = surroundWithHtml('This is a <a href="./link">link</a>');

const wrapper = mountWithIntl(
withWebSocket(
<MemoryRouter>
<Annotator
fileIri={fileIri}
vocabularyIri={vocabularyIri}
{...mockedCallbackProps}
{...stateProps}
initialHtml={htmlContent}
{...intlFunctions()}
/>
</MemoryRouter>
)
);
const sampleOutput = 'This is a <a data-href="./link">link</a>';
expect(wrapper.html().includes(sampleOutput)).toBe(true);
});

Expand Down Expand Up @@ -169,6 +190,39 @@ describe("Annotator", () => {
);
});

it("passes file language to content rendering", () => {
file.language = "en";
const wrapper = shallow<Annotator>(
<Annotator
fileIri={fileIri}
vocabularyIri={vocabularyIri}
{...stateProps}
{...mockedCallbackProps}
initialHtml={generalHtmlContent}
{...intlFunctions()}
/>
);
const contentRenderer = wrapper.find(AnnotatorContent);
expect(contentRenderer.props().annotationLanguage).toEqual(file.language);
});

it("passes provided annotation language to content rendering", () => {
file.language = "en";
const wrapper = shallow<Annotator>(
<Annotator
fileIri={fileIri}
vocabularyIri={vocabularyIri}
{...stateProps}
{...mockedCallbackProps}
initialHtml={generalHtmlContent}
annotationLanguage={"cs"}
{...intlFunctions()}
/>
);
const contentRenderer = wrapper.find(AnnotatorContent);
expect(contentRenderer.props().annotationLanguage).toEqual("cs");
});

describe("on mount", () => {
const selector: TextQuoteSelector = {
exactMatch: "test-term",
Expand Down
22 changes: 22 additions & 0 deletions src/component/annotator/__tests__/HtmlParserUtils.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { Element } from "domhandler";
import HtmlParserUtils from "../HtmlParserUtils";

describe("HtmlParserUtils", () => {
describe("html2dom", () => {
it("remove target and rel attributes added when rendering HTML", () => {
const html =
'<a href="http://example.com" target="_blank" rel="noopener noreferrer">Example</a>';
const nodes = HtmlParserUtils.html2dom(html);
expect((nodes[0] as Element).attribs.href).toEqual("http://example.com");
expect((nodes[0] as Element).attribs.target).not.toBeDefined();
expect((nodes[0] as Element).attribs.rel).not.toBeDefined();
});

it("set href attribute to data-href attribute created when rendering HTML", () => {
const html = '<a data-href="./about.html">Example</a>';
const nodes = HtmlParserUtils.html2dom(html);
expect((nodes[0] as Element).attribs.href).toEqual("./about.html");
expect((nodes[0] as Element).attribs["data-href"]).not.toBeDefined();
});
});
});
2 changes: 2 additions & 0 deletions src/component/file/FileContentDetail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ interface FileDetailProvidedProps {
iri: IRI;
vocabularyIri: IRI;
scrollTo?: TextQuoteSelector; // Selector of an annotation to scroll to (and highlight) after rendering
annotationLanguage?: string;
}

interface FileDetailOwnProps extends HasI18n {
Expand Down Expand Up @@ -129,6 +130,7 @@ export class FileContentDetail extends React.Component<
initialHtml={this.props.fileContent}
scrollTo={this.props.scrollTo}
onUpdate={this.onUpdate}
annotationLanguage={this.props.annotationLanguage}
/>
</>
);
Expand Down
Loading

0 comments on commit faba150

Please sign in to comment.