Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions cypress/e2e/graphwise-reactodia/graphwise-reactodia.cy.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,27 @@ describe('graphwise-reactodia', () => {
// Then the @Watch handler re-renders in place - the workspace is rebuilt, not torn down
GraphwiseReactodiaSteps.getWorkspace().should('exist');
});

it('Should seed the canvas only from the seed present when the workspace mounts', () => {
// Given a workspace mounted without a seed
GraphwiseReactodiaSteps.provideRequiredProps();
GraphwiseReactodiaSteps.getCanvas().should('exist');
// Then nothing should be placed on the canvas
GraphwiseReactodiaSteps.getElements().should('not.exist');

// When, I re-visit the page with a seed
GraphwiseReactodiaSteps.visit();
GraphwiseReactodiaSteps.setSeed();
GraphwiseReactodiaSteps.provideRequiredProps();

// Then each seeded IRI should be placed on the canvas as an element
GraphwiseReactodiaSteps.getElements().should('have.length', 2);

// When, I switch the language to French (effectively reloading the workspace)
GraphwiseReactodiaSteps.switchToFrench();

// Then, the language should be switched, and the number of nodes should be the same, because the layout should be
// preserved before switching
GraphwiseReactodiaSteps.getElements().should('have.length', 2);
});
});
8 changes: 8 additions & 0 deletions cypress/steps/graphwise-reactodia-steps.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ export class GraphwiseReactodiaSteps {
return this.getComponent().find('.reactodia-unified-search__search-input');
}

static getElements() {
return this.getCanvas().find('[data-element-id]');
}

static setQueryFunction() {
cy.getByTestId('set-query-function').click();
}
Expand All @@ -36,6 +40,10 @@ export class GraphwiseReactodiaSteps {
cy.getByTestId('switch-repository').click();
}

static setSeed() {
cy.getByTestId('set-seed').click();
}

static switchToFrench() {
cy.getByTestId('set-language-fr').click();
}
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

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

14 changes: 13 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,23 @@
{
"name": "graphwise-reactodia",
"version": "0.0.1-TR5",
"version": "0.0.1-TR6",
"description": "Graphwise wrapper around the Reactodia graph explorer.",
"license": "Apache-2.0",
"author": "Ontotext AD",
"main": "dist/index.cjs.js",
"module": "dist/esm/index.js",
"collection": "dist/collection/collection-manifest.json",
"exports": {
".": {
"import": "./dist/graphwise-reactodia/graphwise-reactodia.esm.js",
"require": "./dist/index.cjs.js"
},
"./loader": {
"import": "./loader/index.js",
"require": "./loader/index.cjs.js",
"types": "./loader/index.d.ts"
}
},
"files": [
"dist/",
"loader/"
Expand Down
62 changes: 33 additions & 29 deletions src/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,45 +5,49 @@
* It contains typing information for all components that exist in this project.
*/
import { HTMLStencilElement, JSXBase } from "@stencil/core/internal";
import { SparqlDataProviderSettings, SparqlQueryFunction } from "@reactodia/workspace";
export { SparqlDataProviderSettings, SparqlQueryFunction } from "@reactodia/workspace";
import { ReactodiaConfig } from "./components/graphwise-reactodia/models/reactodia-config";
import { LanguageKey } from "./components/graphwise-reactodia/i18n/language-key";
import { SparqlDataProviderSettings } from "@reactodia/workspace";
export { ReactodiaConfig } from "./components/graphwise-reactodia/models/reactodia-config";
export { LanguageKey } from "./components/graphwise-reactodia/i18n/language-key";
export { SparqlDataProviderSettings } from "@reactodia/workspace";
export namespace Components {
/**
* A web component that renders a graph with the Reactodia workspace.
* The component is backed by a SPARQL endpoint but is endpoint-agnostic: the host passes
* the active repository's endpoint via `current-repository` and a `queryFunction` that
* performs the actual HTTP request. Reactodia fetches all node, link and type data lazily
* through them; the canvas starts empty and the user populates it via the search bar.
* the active repository's endpoint via `current-repository` and a `config.queryFunction`
* that performs the actual HTTP request. Reactodia fetches all node, link and type data
* lazily through them; the canvas starts empty and the user populates it via the search bar.
* The component is a thin wrapper around Reactodia: query configuration lives outside the
* wrapper and is supplied through the `providerSettings` prop.
*/
interface GraphwiseReactodia {
/**
* The active repository id. Appended to {@link queryFunction} as the request `url`; changing it re-points the graph at the new repository (and resets the canvas), which is how runtime repository changes are handled.
* Host-supplied configuration: the SPARQL `queryFunction` transport and an optional `seed` set of entities to pre-populate the canvas with. A DOM property (an object, not an attribute) passed in from outside the wrapper. Read once on mount; not watched, as it only sets up the data source and initial canvas.
*/
"currentRepository": string;
"config"?: ReactodiaConfig;
/**
* The active repository id. Appended to {@link queryFunction } as the request `url`; changing it re-points the graph at the new repository (and resets the canvas), which is how runtime repository changes are handled.
*/
"currentRepository"?: string;
/**
* UI language code (e.g. `en`, `fr`) for the Reactodia interface. Defaults to English.
* @default 'en'
* @default LanguageKey.EN
*/
"language": string;
"language": LanguageKey;
/**
* Query preset for the SPARQL data provider, owned and configured by the host. A DOM property (an object, not an attribute) passed in from outside the wrapper. When omitted, the data provider falls back to Reactodia's generic OWL/RDFS preset. Changing it rebuilds the data provider and resets the canvas.
*/
"providerSettings"?: SparqlDataProviderSettings;
/**
* HTTP transport for the SPARQL requests. Set by the host so requests go through the host's HTTP layer (auth, interceptors) instead of a built-in `fetch`.
*/
"queryFunction": SparqlQueryFunction;
}
}
declare global {
/**
* A web component that renders a graph with the Reactodia workspace.
* The component is backed by a SPARQL endpoint but is endpoint-agnostic: the host passes
* the active repository's endpoint via `current-repository` and a `queryFunction` that
* performs the actual HTTP request. Reactodia fetches all node, link and type data lazily
* through them; the canvas starts empty and the user populates it via the search bar.
* the active repository's endpoint via `current-repository` and a `config.queryFunction`
* that performs the actual HTTP request. Reactodia fetches all node, link and type data
* lazily through them; the canvas starts empty and the user populates it via the search bar.
* The component is a thin wrapper around Reactodia: query configuration lives outside the
* wrapper and is supplied through the `providerSettings` prop.
*/
Expand All @@ -61,30 +65,30 @@ declare namespace LocalJSX {
/**
* A web component that renders a graph with the Reactodia workspace.
* The component is backed by a SPARQL endpoint but is endpoint-agnostic: the host passes
* the active repository's endpoint via `current-repository` and a `queryFunction` that
* performs the actual HTTP request. Reactodia fetches all node, link and type data lazily
* through them; the canvas starts empty and the user populates it via the search bar.
* the active repository's endpoint via `current-repository` and a `config.queryFunction`
* that performs the actual HTTP request. Reactodia fetches all node, link and type data
* lazily through them; the canvas starts empty and the user populates it via the search bar.
* The component is a thin wrapper around Reactodia: query configuration lives outside the
* wrapper and is supplied through the `providerSettings` prop.
*/
interface GraphwiseReactodia {
/**
* The active repository id. Appended to {@link queryFunction} as the request `url`; changing it re-points the graph at the new repository (and resets the canvas), which is how runtime repository changes are handled.
* Host-supplied configuration: the SPARQL `queryFunction` transport and an optional `seed` set of entities to pre-populate the canvas with. A DOM property (an object, not an attribute) passed in from outside the wrapper. Read once on mount; not watched, as it only sets up the data source and initial canvas.
*/
"config"?: ReactodiaConfig;
/**
* The active repository id. Appended to {@link queryFunction } as the request `url`; changing it re-points the graph at the new repository (and resets the canvas), which is how runtime repository changes are handled.
*/
"currentRepository"?: string;
/**
* UI language code (e.g. `en`, `fr`) for the Reactodia interface. Defaults to English.
* @default 'en'
* @default LanguageKey.EN
*/
"language"?: string;
"language"?: LanguageKey;
/**
* Query preset for the SPARQL data provider, owned and configured by the host. A DOM property (an object, not an attribute) passed in from outside the wrapper. When omitted, the data provider falls back to Reactodia's generic OWL/RDFS preset. Changing it rebuilds the data provider and resets the canvas.
*/
"providerSettings"?: SparqlDataProviderSettings;
/**
* HTTP transport for the SPARQL requests. Set by the host so requests go through the host's HTTP layer (auth, interceptors) instead of a built-in `fetch`.
*/
"queryFunction"?: SparqlQueryFunction;
}
interface IntrinsicElements {
"graphwise-reactodia": GraphwiseReactodia;
Expand All @@ -97,9 +101,9 @@ declare module "@stencil/core" {
/**
* A web component that renders a graph with the Reactodia workspace.
* The component is backed by a SPARQL endpoint but is endpoint-agnostic: the host passes
* the active repository's endpoint via `current-repository` and a `queryFunction` that
* performs the actual HTTP request. Reactodia fetches all node, link and type data lazily
* through them; the canvas starts empty and the user populates it via the search bar.
* the active repository's endpoint via `current-repository` and a `config.queryFunction`
* that performs the actual HTTP request. Reactodia fetches all node, link and type data
* lazily through them; the canvas starts empty and the user populates it via the search bar.
* The component is a thin wrapper around Reactodia: query configuration lives outside the
* wrapper and is supplied through the `providerSettings` prop.
*/
Expand Down
33 changes: 19 additions & 14 deletions src/components/graphwise-reactodia/graphwise-reactodia.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
import {Component, Element, h, Host, Prop, Watch} from '@stencil/core';
import {Root} from 'react-dom/client';
import {SerializedDiagram, SparqlDataProviderSettings, SparqlQueryFunction} from '@reactodia/workspace';
import {SerializedDiagram, SparqlDataProviderSettings} from '@reactodia/workspace';
import {exportReactodiaLayout, mountReactodia, unmountReactodia, updateReactodia} from './reactodia-app';
import {LanguageKey} from './i18n/language-key';
import {ReactodiaConfig} from './models/reactodia-config';

/**
* A web component that renders a graph with the Reactodia workspace.
*
* The component is backed by a SPARQL endpoint but is endpoint-agnostic: the host passes
* the active repository's endpoint via `current-repository` and a `queryFunction` that
* performs the actual HTTP request. Reactodia fetches all node, link and type data lazily
* through them; the canvas starts empty and the user populates it via the search bar.
* the active repository's endpoint via `current-repository` and a `config.queryFunction`
* that performs the actual HTTP request. Reactodia fetches all node, link and type data
* lazily through them; the canvas starts empty and the user populates it via the search bar.
*
* The component is a thin wrapper around Reactodia: query configuration lives outside the
* wrapper and is supplied through the `providerSettings` prop.
Expand All @@ -19,25 +21,28 @@ import {exportReactodiaLayout, mountReactodia, unmountReactodia, updateReactodia
styleUrl: 'graphwise-reactodia.scss',
})
export class GraphwiseReactodia {
@Element() private readonly hostElement: HTMLElement;
@Element() private readonly hostElement?: HTMLElement;

/**
* The active repository id. Appended to {@link queryFunction} as the request `url`;
* changing it re-points the graph at the new repository (and resets the canvas),
* which is how runtime repository changes are handled.
*/
@Prop() currentRepository: string;
@Prop() currentRepository?: string;

/**
* HTTP transport for the SPARQL requests. Set by the host so requests go through the host's HTTP layer (auth,
* interceptors) instead of a built-in `fetch`.
* Host-supplied configuration: the SPARQL `queryFunction` transport and an optional `seed`
* set of entities to pre-populate the canvas with. A DOM property (an object, not an
* attribute) passed in from outside the wrapper.
*
* Read once on mount; not watched, as it only sets up the data source and initial canvas.
*/
@Prop() queryFunction: SparqlQueryFunction;
@Prop() config?: ReactodiaConfig;

/**
* UI language code (e.g. `en`, `fr`) for the Reactodia interface. Defaults to English.
*/
@Prop() language = 'en';
@Prop() language = LanguageKey.EN;

/**
* Query preset for the SPARQL data provider, owned and configured by the host. A DOM
Expand All @@ -47,7 +52,7 @@ export class GraphwiseReactodia {
*/
@Prop() providerSettings?: SparqlDataProviderSettings;

private reactRoot: Root;
private reactRoot?: Root;

@Watch('currentRepository')
@Watch('providerSettings')
Expand Down Expand Up @@ -86,8 +91,8 @@ export class GraphwiseReactodia {
throw new Error('currentRepository is required');
}

if (!this.queryFunction) {
throw new Error('queryFunction is required');
if (!this.config?.queryFunction) {
throw new Error('config.queryFunction is required');
}

if (!this.hostElement) {
Expand All @@ -97,7 +102,7 @@ export class GraphwiseReactodia {
const props = {
initialDiagram,
currentRepository: this.currentRepository,
queryFunction: this.queryFunction,
config: this.config,
language: this.language,
providerSettings: this.providerSettings,
};
Expand Down
4 changes: 4 additions & 0 deletions src/components/graphwise-reactodia/i18n/language-key.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export enum LanguageKey {
EN = 'en',
FR = 'fr'
}
10 changes: 8 additions & 2 deletions src/components/graphwise-reactodia/i18n/translations.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import frTranslation from './fr.reactodia.translation.json';
import {LanguageKey} from './language-key';

export const TRANSLATIONS = {
'fr': frTranslation
type TranslationBundle = {
[language: string]: Record<string, TranslationBundle | string | null>;
};

// English is provided by default from reactodia so we don't need to include it here, unless we override it
export const TRANSLATIONS: Partial<Record<LanguageKey, TranslationBundle>> = {
[LanguageKey.FR]: frTranslation
}
18 changes: 10 additions & 8 deletions src/components/graphwise-reactodia/models/reactodia-app-props.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,28 @@
import {SerializedDiagram, SparqlDataProviderSettings, SparqlQueryFunction} from '@reactodia/workspace';
import {SerializedDiagram, SparqlDataProviderSettings} from '@reactodia/workspace';
import {LanguageKey} from '../i18n/language-key';
import {ReactodiaConfig} from './reactodia-config';

/**
* Props for the internal Reactodia React application.
*/
export interface ReactodiaAppProps {
/**
* The active repository id. Passed to the SPARQL data provider as its `endpointUrl` and
* surfaced to {@link queryFunction} as the request `url`, which the host transport uses to
* target the repository. Changing it re-points the diagram at a different repository.
* surfaced to the {@link ReactodiaConfig.queryFunction} as the request `url`, which the host
* transport uses to target the repository. Changing it re-points the diagram at a different
* repository.
*/
currentRepository: string;
/**
* Transport for the SPARQL requests. The host owns the connection (auth, interceptors,
* base URL) inside this function; Reactodia delegates every SPARQL call to it instead
* of using the built-in `fetch`.
* Host-supplied configuration read once on mount: the SPARQL transport and the optional
* set of entities to pre-populate the canvas with.
*/
queryFunction: SparqlQueryFunction;
config: ReactodiaConfig;
/**
* UI language code (e.g. `en`, `fr`); selects the Reactodia translation bundle
* and is also used as the initial graph-data language.
*/
language: string;
language: LanguageKey;
/**
* A previously exported diagram to restore on mount. Used to carry the user's current
* canvas across a remount (e.g. forced by a language change). When omitted, the canvas
Expand Down
24 changes: 24 additions & 0 deletions src/components/graphwise-reactodia/models/reactodia-config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import {SparqlQueryFunction} from '@reactodia/workspace';

/**
* Host-supplied configuration read once when the workspace mounts (not reactive).
*
* Groups the inputs the host wires up before the graph is created: the SPARQL transport
* and the optional set of entities to pre-populate the canvas with. Passed to the
* `graphwise-reactodia` component as a single DOM property (an object, not an attribute).
*/
export interface ReactodiaConfig {
/**
* HTTP transport for the SPARQL requests. Set by the host so requests go through the host's
* HTTP layer (auth, interceptors) instead of a built-in `fetch`.
*/
queryFunction: SparqlQueryFunction;
/**
* IRIs of the entities to place on the canvas on startup. When provided, each IRI is added
* as an element and its data (labels, types, properties) and links are resolved from the
* SPARQL provider, then the workspace lays them out. Note that when using this seed method reactodia will request data
* from the connected sparql endpoint, so if the resources are not in the DB, then this approach will not work.
* (e.g. in the case of a construct query)
*/
seedIris?: string[];
}
Loading