Source

guides/visual-graph/visual-graph-config-create.js

const VISUAL_GRAPH_EXPLORE_TITLE = 'guide.step-action.visual-graph';

/**
 * @name visual-graph-config-create
 * @memberof module:Interactive Guide
 *
 * @description
 * This is a complex step that guides the user through the full process of creating a new visual graph configuration.
 * It includes navigating to the visual graph page, creating and naming the configuration, optionally adding a description
 * and hint, configuring tabs with starting points, SPARQL queries, or resource IRIs, and saving the configuration.
 *
 * The step sequence is dynamic and depends on the provided options. Some steps are always included, while others
 * appear only when certain options are provided.
 *
 * **Always included steps:**
 *
 * Navigate to the visual graph page via the Explore main menu<br>
 * <img src="resources/guides/main-menu/main-menu-explore.png" style="height:200px; border: solid; border-width:1px"/><br>
 *
 * Click the button to create a new configuration<br>
 * <img src="resources/guides/visual-graph/visual-graph-config-create-click.png" style="height:200px; border: solid; border-width:1px"/><br>
 *
 * Enter a name for the configuration<br>
 * <img src="resources/guides/visual-graph/visual-graph-config-name.png" style="height:200px; border: solid; border-width:1px"/><br>
 *
 * Introduction to the starting point configuration<br>
 * <img src="resources/guides/visual-graph/visual-graph-config-starting-point-intro.png" style="height:200px; border: solid; border-width:1px"/><br>
 *
 * **Optional steps (based on options):**
 *
 * Enter a description for the configuration (when `options.configDescription` is provided)<br>
 * <img src="resources/guides/visual-graph/visual-graph-config-description.png" style="height:200px; border: solid; border-width:1px"/><br>
 *
 * Enter a hint IRI for the configuration (when `options.configHint` is provided)<br>
 * <img src="resources/guides/visual-graph/visual-graph-config-hint.png" style="height:200px; border: solid; border-width:1px"/><br>
 *
 * **Tab configuration steps (based on `options.tabConfig`):**
 *
 * If `options.tabConfig` is not provided or is empty, the step sequence ends after the starting point intro.
 * Otherwise, for each entry in the `tabConfig` array, the following steps are added:
 *
 * Click on a configuration tab (skipped if the tab is already active)<br>
 * <img src="resources/guides/visual-graph/visual-graph-config-click-tab.png" style="height:200px; border: solid; border-width:1px"/><br>
 *
 * Select a starting point type (when `tabConfig.startingPoint` is provided)<br>
 * <img src="resources/guides/visual-graph/visual-graph-config-starting-point-search.png" style="height:200px; border: solid; border-width:1px"/><br>
 * <img src="resources/guides/visual-graph/visual-graph-config-starting-point-fixed.png" style="height:200px; border: solid; border-width:1px"/><br>
 * <img src="resources/guides/visual-graph/visual-graph-config-starting-point-query.png" style="height:200px; border: solid; border-width:1px"/><br>
 *
 * Search for a resource IRI (when `tabConfig.iri` and `tabConfig.searchTerm` are provided).
 * **Cannot be combined with `tabConfig.query`** — only one of `iri`/`searchTerm` or `query` should be set per tab config entry.<br>
 * <img src="resources/guides/resource/resource-search-rdf.png" style="height:200px; border: solid; border-width:1px"/><br>
 *
 * Select the resource from autocomplete results<br>
 * <img src="resources/guides/resource/resource-search-autocomplete-item.png" style="height:200px; border: solid; border-width:1px"/><br>
 *
 * Enter a SPARQL query (when `tabConfig.query` is provided).
 * **Cannot be combined with `tabConfig.iri`/`tabConfig.searchTerm`** — only one of `query` or `iri`/`searchTerm` should be set per tab config entry.<br>
 * <img src="resources/guides/sparql-editor/sparql-editor.png" style="height:200px; border: solid; border-width:1px"/><br>
 *
 * Toggle include inferred data (when `tabConfig.includeInferredData` is provided)<br>
 * <img src="resources/guides/sparql-editor/sparql-editor-include-inferred-data.png" style="height:200px; border: solid; border-width:1px"/><br>
 *
 * Toggle expand results over owl:sameAs (when `tabConfig.expandResultsOverOwl` is provided)<br>
 * <img src="resources/guides/sparql-editor/sparql-editor-expand-results-over-owl.png" style="height:200px; border: solid; border-width:1px"/><br>
 *
 * View sample queries (when `tabConfig.sampleQueryContent` is provided)<br>
 * <img src="resources/guides/visual-graph/visual-graph-config-sample-query.png" style="height:200px; border: solid; border-width:1px"/><br>
 *
 * **Post-configuration steps (only when `options.tabConfig` is provided):**
 *
 * Share the configuration with other users (when `options.shareConfig` is `true`)<br>
 * <img src="resources/guides/visual-graph/visual-graph-config-share.png" style="height:200px; border: solid; border-width:1px"/><br>
 *
 * Save the configuration<br>
 * <img src="resources/guides/visual-graph/visual-graph-config-save.png" style="height:200px; border: solid; border-width:1px"/><br>
 *
 * Select the newly created configuration<br>
 * <img src="resources/guides/visual-graph/visual-graph-config-select.png" style="height:200px; border: solid; border-width:1px"/><br>
 *
 * Open graph settings<br>
 * <img src="resources/guides/visual-graph/visual-graph-settings-click.png" style="height:200px; border: solid; border-width:1px"/><br>
 *
 * Set the maximum number of links<br>
 * <img src="resources/guides/visual-graph/visual-graph-set-maximum-links.png" style="height:200px; border: solid; border-width:1px"/><br>
 *
 * Save the graph settings<br>
 * <img src="resources/guides/visual-graph/visual-graph-settings-save.png" style="height:200px; border: solid; border-width:1px"/><br>
 *
 * Expand a node in the visual graph (when `options.expandIri` is provided)<br>
 * <img src="resources/guides/visual-graph/visual-graph-expand.png" style="height:200px; border: solid; border-width:1px"/><br>
 *
 * Zoom the visual graph<br>
 * <img src="resources/guides/visual-graph/visual-graph-zoom.png" style="height:200px; border: solid; border-width:1px"/><br>
 *
 * The step can be configured using the common options defined in [Options](#.Options).
 *
 * @property {string} [options.configDescription] - Optional. The description for the configuration. When provided, the description step is included.
 * @property {string} [options.configHint] - Optional. The hint IRI for the configuration. When provided, the hint step is included.
 * @property {boolean} [options.shareConfig] - Optional. When `true`, the share configuration step is included.
 * @property {Object[]} [options.tabConfig] - Optional. An array of tab configuration objects. When not provided or empty, the step ends early without saving.
 * @property {string} options.tabConfig[].tabName - The name of the tab to click. Must be one of `startingPoint`, `graphExpansion`, `nodeBasics`, `edgeBasics`, or `nodeExtra`.
 * @property {string} [options.tabConfig[].startingPoint] - Optional. The starting point type to select.
 * @property {string} [options.tabConfig[].iri] - Optional. The IRI of the resource to search for. Must be used together with `searchTerm`. Cannot be used together with `query`.
 * @property {string} [options.tabConfig[].searchTerm] - Optional. The search term used to find the resource. Must be used together with `iri`. Cannot be used together with `query`.
 * @property {string} [options.tabConfig[].query] - Optional. The SPARQL query to enter. Cannot be used together with `iri`/`searchTerm`.
 * @property {boolean} [options.tabConfig[].includeInferredData] - Optional. When provided, the include inferred data step is shown.
 * @property {boolean} [options.tabConfig[].expandResultsOverOwl] - Optional. When provided, the expand results over owl:sameAs step is shown.
 * @property {string} [options.tabConfig[].sampleQueryContent] - Optional. When provided, the sample query step is shown with this content.
 * @property {number} [options.linkLimit] - The maximum number of links to set in the graph settings.
 * @property {string} [options.expandIri] - Optional. The IRI of the node to expand in the visual graph. When provided, the expand step is included.
 * @property {string} [options.iriLabel] - Optional. The label to display for the node to expand. When provided, the expand step is included.
 *
 * @example
 * ```JSON
 * {
 *   "guideBlockName": "visual-graph-config-create",
 *   "options": {
 *     "configDescription": "A visual graph for exploring RDF data.",
 *     "configHint": "http://example.org/resource",
 *     "shareConfig": true,
 *     "expandIri": "https://swapi.co/resource/Human/6/",
 *     "iriLabel": "Leia Organa",
 *     "linkLimit": 20,
 *     "tabConfig": [
 *       {
 *            "tabName": "startingPoint",
 *            "startingPoint": "fixed",
 *            "searchTerm": "luke",
 *            "iri": "https://swapi.co/resource/human/1"
 *        },
 *        {
 *            "tabName": "graphExpansion",
 *            "query": "PREFIX wev: <http://weverify.eu/ontology/>\nPREFIX schema: <http://schema.org/>\nconstruct {\n    ?node ?p ?o .\n    ?s ?p ?node .\n} where {\n    values ?p {\n        schema:hasPart\n        schema:appearance\n        schema:itemReviewed\n        schema:mentionedIn\n        schema:author\n        schema:publisher\n    }\n    { ?node ?p ?o . filter not exists {?o a wev:SupportingEvidence . } }\n    union\n    { ?s ?p ?node . }\n}",
 *            "queryExtraContent": "The graph expansion query defines what happens when a node is expanded in the visual graph. Expanding a node loads additional resources connected to it and adds them to the graph as new nodes and edges.",
 *            "sampleQueryContent": "The <b>Sample queries</b>  includes example graph expansion queries. <b>Unfiltered object properties</b> shows all object property connections of a node, while <b>Filtered object properties</b> shows only selected ones.",
 *            "includeInferredData": true,
 *            "expandResultsOverOwl": true
 *        },
 *        {
 *            "tabName": "nodeBasics",
 *            "query": "PREFIX skos: <http://www.w3.org/2004/02/skos/core#>\nPREFIX : <http://data.europa.eu/949/>\nselect * {\n    ?node :opType/skos:prefLabel ?type\n    filter (lang(?type)=\"en\")\n    ?node :opName ?name; :uopid ?id\n    bind(concat(?name,\"\\n\",?type,\": \",?id) as ?label)\n}",
 *            "queryExtraContent": "The <b>Node basics</b> query retrieves basic properties of a node and maps them to visual attributes such as label, type, description, and rank used when rendering the node in the graph.",
 *            "sampleQueryContent": "The Sample queries includes ready examples of Node basics queries. Direct type only uses only the node’s type to determine its color. Direct type, label and rank also retrieves a label to display as the node name and a rank value that controls the node size."
 *        },
 *        {
 *            "tabName": "edgeBasics",
 *            "query": "PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>\nPREFIX skos: <http://www.w3.org/2004/02/skos/core#>\n\nSELECT ?label {\n    ?edge rdfs:label | skos:prefLabel ?label .\n}",
 *            "queryExtraContent": "This Edge Basics defines the text shown on the edges in the visual graph. It retrieves a label for each relationship so that the edge displays a clear name instead of the full predicate IRI.",
 *            "sampleQueryContent": "The Sample queries provides example queries for retrieving labels for edges. RDFS or SKOS label displays the label stored in rdfs:label or skos:prefLabel. RDFS or SKOS label in English displays the label only when it is available in English."
 *        },
 *        {
 *            "tabName": "nodeExtra",
 *            "query": "SELECT ?property ?value {\n    ?node ?property ?value .\nFILTER(isLiteral(?value))\n}",
 *            "queryExtraContent": "This Node extra defines the text shown on the nodes in the visual graph. It retrieves a label for each relationship so that the edge displays a clear name instead of the full predicate IRI.",
 *            "sampleQueryContent": "The Sample queries provides examples for retrieving node details. Datatype properties shows properties with literal values, while Image and datatype properties also includes an image URL."
 *        }
 *     ]
 *   }
 * }
 * ```
 */
const step = {
  guideBlockName: 'visual-graph-config-create',
  getSteps: function(options, services) {
    const translate = services.translate;
    const GuideUtils = services.GuideUtils;
    options.title = options.title ?? translate(this.translationBundle, VISUAL_GRAPH_EXPLORE_TITLE);

    const steps = [
      {
        guideBlockName: 'click-main-menu',
        options: {
          menu: 'visual-graph',
          showIntro: true,
          ...options
        }
      },
      {
        guideBlockName: 'visual-graph-config-create-click',
        options: {...options}
      },
      {
        guideBlockName: 'visual-graph-config-name',
        options: {...options}
      }
    ];

    const configDescription = options.configDescription;
    if (configDescription) {
      steps.push({
        guideBlockName: 'visual-graph-config-description',
        options: {...options}
      });
    }

    const configHint = options.configHint;
    if (configHint) {
      steps.push({
        guideBlockName: 'visual-graph-config-hint',
        options: {...options}
      });
    }

    steps.push({
      guideBlockName: 'visual-graph-config-starting-point-intro',
      options: {...options}
    });

    if (!options.tabConfig?.length) {
      // If we don't configure the graph, we can't proceed to save and view it
      return steps;
    }

    (options.tabConfig ?? []).forEach((tabConfig) => {
      // each tab config represents a tab click and the related steps to add in that specific tab
      steps.push({
        guideBlockName: 'visual-graph-config-click-tab',
        options: {
          scrollOffset: GuideUtils.SCROLL_OFFSET.START,
          ...options,
          ...tabConfig
        }
      });

      if (tabConfig.startingPoint) {
        steps.push({
          guideBlockName: 'visual-graph-config-starting-point',
          options: {...options, ...tabConfig}
        });
      }

      // we can't have iri and query at the same time. They are not simultaneously available in the view
      if (tabConfig.iri && tabConfig.searchTerm) {
        steps.push({
          guideBlockName: 'resource-search-rdf',
          options: {
            url: 'graphs-visualizations/config/save',
            ...options,
            ...tabConfig
          }
        });
        steps.push({
          guideBlockName: 'resource-search-autocomplete-item',
          options: {
            url: 'graphs-visualizations/config/save',
            ...options,
            ...tabConfig
          }
        });
      } else if (tabConfig.query) {
        steps.push({
          guideBlockName: 'sparql-editor',
          options: {
            url: 'graphs-visualizations/config/save',
            scrollOffset: GuideUtils.SCROLL_OFFSET.CENTER,
            ...options,
            ...tabConfig
          }
        });
      }

      if (tabConfig.includeInferredData) {
        steps.push({
          guideBlockName: 'sparql-editor-include-inferred-data',
          options: {
            url: 'graphs-visualizations/config/save',
            ...options,
            ...tabConfig
          }
        });
      }

      if (tabConfig.expandResultsOverOwl) {
        steps.push({
          guideBlockName: 'sparql-editor-expand-results-over-owl',
          options: {
            url: 'graphs-visualizations/config/save',
            ...options,
            ...tabConfig
          }
        });
      }

      if (tabConfig.sampleQueryContent) {
        steps.push({
          guideBlockName: 'visual-graph-config-sample-query',
          options: {
            ...options,
            ...tabConfig,
            content: tabConfig.sampleQueryContent
          }
        });
      }
    });

    if (options.shareConfig) {
      steps.push({
        guideBlockName: 'visual-graph-config-share',
        options: {...options}
      });
    }

    steps.push({
      guideBlockName: 'visual-graph-config-save',
      options: {...options}
    });

    steps.push({
      guideBlockName: 'visual-graph-config-select',
      options: {...options}
    });

    steps.push({
      guideBlockName: 'visual-graph-settings-click',
      options: {...options}
    });

    steps.push({
      guideBlockName: 'visual-graph-set-maximum-links',
      options: {...options}
    });

    steps.push({
      guideBlockName: 'visual-graph-settings-save',
      options: {...options}
    });

    if (options.expandIri) {
      steps.push({
        guideBlockName: 'visual-graph-expand',
        options: {
          ...options,
          iri: options.expandIri
        }
      });
    }

    steps.push({
      guideBlockName: 'visual-graph-zoom',
      options: {...options}
    });

    return steps;
  },
  translationBundle: {
    en: {
      [VISUAL_GRAPH_EXPLORE_TITLE]: 'Visual graph explore'
    },
    fr: {
      [VISUAL_GRAPH_EXPLORE_TITLE]: 'Explorer le graphique visuel'
    }
  }
};

export function register(registry) {
  registry.add('guide.step', step);
}