diff --git a/cypress/e2e/spec.cy.js b/cypress/e2e/spec.cy.js
index 6c97e556..28e10a8c 100644
--- a/cypress/e2e/spec.cy.js
+++ b/cypress/e2e/spec.cy.js
@@ -69,6 +69,14 @@ describe("Web app", () => {
);
});
+ it("Variables in column header contain link to ontology", () => {
+ cy.visit("/");
+
+ cy.contains("My favourite musicians").click();
+ cy.contains("Finished in:");
+ cy.get('a[href="http://schema.org/name"]');
+ })
+
it("When one source throws an error, the results of other sources are still shown", () => {
cy.visit("/");
diff --git a/src/components/ListResultTable/QueryResultList/QueryResultList.jsx b/src/components/ListResultTable/QueryResultList/QueryResultList.jsx
index e652a5ff..797f5128 100644
--- a/src/components/ListResultTable/QueryResultList/QueryResultList.jsx
+++ b/src/components/ListResultTable/QueryResultList/QueryResultList.jsx
@@ -4,6 +4,7 @@ import ActionBar from "../../ActionBar/ActionBar";
import GenericField from "../../../representationProvider/GenericField";
import { Term } from "sparqljs";
import config from "../../../config";
+import TableHeader from "./TableHeader/TableHeader";
/**
* @param {object} props - the props passed down to the component
@@ -25,7 +26,7 @@ function QueryResultList(props) {
} {...props}>
{values && (
-
+ }>
{Object.keys(values).map((key) => {
return (
} props.children - the children of the component
+ * @param {object} props.config - the config object of the application
+ * @returns {Component} the header of the table containing the column names, the sort icons and ontology links
+ */
+function TableHeader({ children, config }) {
+ const { sort, setSort, resource } = useListContext();
+ const { variableOntology } = config.queries.filter(
+ (query) => query.id === resource
+ )[0];
+
+ /**
+ * Handles the click on a header and sets the sort state accordingly
+ * @param {string} target - the source of the column that was clicked
+ */
+ function handleHeaderClick(target) {
+ const newSort = { field: target, order: "DESC" };
+ if (sort) {
+ if (sort.order === "ASC") {
+ newSort.order = "DESC";
+ } else {
+ newSort.order = "ASC";
+ }
+ }
+ setSort(newSort);
+ }
+
+ return (
+
+
+
+ {React.Children.map(children, (child) => (
+ <>
+ *": { verticalAlign: "middle" } }}
+ >
+ handleHeaderClick(child.props.source)}
+ >
+ {child.props.label}
+
+ {variableOntology[child.props.source] && (
+ *": { verticalAlign: "middle" } }}
+ >
+
+
+ )}
+ {sort.field === child.props.source && (
+ <>
+ {sort && sort.order === "DESC" && (
+
+ )}
+ {sort && sort.order === "ASC" && (
+
+ )}
+ >
+ )}
+
+ >
+ ))}
+
+
+ );
+}
+
+TableHeader.propTypes = {
+ children: PropTypes.node,
+ config: PropTypes.object.isRequired,
+
+}
+export default TableHeader;
diff --git a/src/dataProvider/SparqlDataProvider.js b/src/dataProvider/SparqlDataProvider.js
index 5b25d5de..546de586 100644
--- a/src/dataProvider/SparqlDataProvider.js
+++ b/src/dataProvider/SparqlDataProvider.js
@@ -94,6 +94,9 @@ async function fetchQuery(query) {
const rawText = await result.text();
query.rawText = rawText;
const parsedQuery = parser.parse(rawText);
+ if (!query.variableOntology) {
+ query.variableOntology = findPredicates(parsedQuery);
+ }
if (!parsedQuery.limit) {
parsedQuery.limit = query.limit;
}
@@ -116,6 +119,26 @@ async function fetchQuery(query) {
}
}
+/**
+ * Given a query and an object, this function returns the predicate of the object in the query.
+ * @param {object} query - the paresed query in which the predicate is to be looked for.
+ * @returns {object} an object with the variable as key and the predicate as value.
+ */
+function findPredicates(query) {
+ const ontologyMapper = {};
+ if (!query.variables) {
+ return query;
+ }
+ for (const part of query.where) {
+ for (const triple of part.triples) {
+ if(triple.predicate.termType !== "Variable"){
+ ontologyMapper[triple.object.value] = triple.predicate.value;
+ }
+ }
+ }
+ return ontologyMapper;
+}
+
/**
* A function that executes a given query and processes every result.
* @param {object} query - the query which is to be executed and additional information about the query.
@@ -125,12 +148,9 @@ async function executeQuery(query) {
try {
query.queryText = await fetchQuery(query);
return handleQueryExecution(
- await myEngine.query(
- query.queryText,
- {
- ...generateContext(query.comunicaContext)
- }
- ),
+ await myEngine.query(query.queryText, {
+ ...generateContext(query.comunicaContext),
+ }),
query
);
} catch (error) {