From 2f9df277df821b200522d19789ac5a7593603668 Mon Sep 17 00:00:00 2001 From: frostyfan109 Date: Thu, 12 Sep 2024 14:00:55 -0400 Subject: [PATCH] add final tour steps --- src/contexts/tour-context/context.tsx | 137 ++++++++++++++++++++++++-- src/hooks/use-synthetic-dom-mask.tsx | 24 +++-- 2 files changed, 148 insertions(+), 13 deletions(-) diff --git a/src/contexts/tour-context/context.tsx b/src/contexts/tour-context/context.tsx index 3ca7c898..09751cc3 100644 --- a/src/contexts/tour-context/context.tsx +++ b/src/contexts/tour-context/context.tsx @@ -56,6 +56,7 @@ const waitForSelector = async (selector: string, errorSelector?: string, timeout } const getExpandButton = () => document.querySelector(`.result-card:first-child span.anticon-expand`) +const getConceptViewRadioOption = () => document.querySelector(`.search-layout-radio-group > label:nth-child(1)`) const getVariableViewRadioOption = () => document.querySelector(`.search-layout-radio-group > label:nth-child(2)`) export const TourProvider = ({ children }: ITourProvider ) => { @@ -79,6 +80,24 @@ export const TourProvider = ({ children }: ITourProvider ) => { const resultCardDomMask = useSyntheticDOMMask(".result-card:first-child", { blockClicks: false }) const resultModalDomMask = useSyntheticDOMMask(".concept-modal > .ant-modal-content", { blockClicks: false }) const variableRadioOptionDomMask = useSyntheticDOMMask(".search-layout-radio-group > label:nth-child(2)", { blockClicks: false }) + const studyListItemDomMask = useSyntheticDOMMask(".variables-collapse > div:first-child", { + blockClicks: false, + padding: { + top: 0, + bottom: 0, + left: 32, + right: 32 + } + }) + const supportNavDomMask = useSyntheticDOMMask(`.helx-header ul.ant-menu > li[data-menu-id$="/support"] > span`, { + blockClicks: false, + padding: { + top: 24, + bottom: 24, + left: 12, + right: 12 + } + }) const tourOptions = useMemo(() => ({ defaultStepOptions: { @@ -125,7 +144,7 @@ export const TourProvider = ({ children }: ITourProvider ) => {
HSS is an open-access search engine for exploring the HEAL research landscape.

HSS is not the search you use every day - it uses linked knowledge to return concepts related - to your search term. This enables researchers to find biomedical concepts of interest + to your search. This enables researchers to find biomedical concepts of interest in the HEAL space such as diseases, phenotypes, anatomical parts, and drugs. Click next to begin a tour.
@@ -192,7 +211,7 @@ export const TourProvider = ({ children }: ITourProvider ) => { attachTo: { element: "head" }, - title: "Concept overview", + title: "Concept search", text: renderToStaticMarkup(
When a user searches for a term, HSS returns relevant biomedical concepts in the form of concept cards. @@ -200,7 +219,7 @@ export const TourProvider = ({ children }: ITourProvider ) => { are related to the concept.

In HSS, a concept is a named entity defined in an ontology or another formal knowledge source. This means - the biomedical concept, and its relation to the search term, comes from a well-defined, established source. + the biomedical concept, and its relation to the search query, comes from a well-defined, established source.
), beforeShowPromise: async () => { @@ -485,9 +504,9 @@ export const TourProvider = ({ children }: ITourProvider ) => { text: renderToStaticMarkup(
HSS also offers variable-level searching. Click on the Variables button - shows variables containing the search query or synonyms grouped by study. + shows variables matching the search terms or synonyms grouped by study. Similar to concepts, variable results are scored based on the level of the match - between variable information (name, description, related terms) and the search term. + between variable information (name, description, related terms) and the search terms.
), buttons: [ @@ -499,7 +518,11 @@ export const TourProvider = ({ children }: ITourProvider ) => { { classes: 'shepherd-button-primary', text: 'Next', - type: 'next' + action: function() { + const variableViewOption = getVariableViewRadioOption()! + this.next() + variableViewOption.click() + } } ], when: (() => { @@ -540,7 +563,107 @@ export const TourProvider = ({ children }: ITourProvider ) => { } })() }, - + { + id: "main.search.variable-view.intro", + attachTo: { + element: "head" + }, + title: "Variable search", + text: renderToStaticMarkup( +
+ Variable search returns all the variables with a match with the search term. + HSS provides a histogram filter to zoom in on higher-scoring results (those more + relevant to the search query). Hovering the mouse pointer over each variable in the + histogram displays additional information, including its name, description, and study. +
+ ), + buttons: [ + { + classes: 'shepherd-button-secondary', + text: 'Back', + action: function() { + const conceptGridViewOption = getConceptViewRadioOption()! + this.back() + conceptGridViewOption.click() + } + }, + { + classes: 'shepherd-button-primary', + text: 'Next', + type: 'next' + } + ], + }, + { + id: "main.search.variable-view.study-list", + attachTo: { + element: studyListItemDomMask.selector, + on: "bottom" + }, + scrollToHandler: () => scrollIntoViewIfNeeded(studyListItemDomMask.originalSelector), + title: "", + text: renderToStaticMarkup( +
+ You can also click to expand each study to view the study variables associated + with the search query. +
+ ), + buttons: [ + { + classes: 'shepherd-button-secondary', + text: 'Back', + type: 'back' + }, + { + classes: 'shepherd-button-primary', + text: 'Next', + type: 'next' + } + ], + when: { + show: () => { studyListItemDomMask.showMask() }, + hide: () => { studyListItemDomMask.hideMask() }, + cancel: () => { studyListItemDomMask.hideMask() }, + complete: () => { studyListItemDomMask.hideMask() } + } + }, + { + id: "main.outro", + attachTo: { + element: supportNavDomMask.selector, + on: "bottom" + }, + scrollToHandler: () => scrollIntoViewIfNeeded(supportNavDomMask.originalSelector), + title: "Conclusion", + text: renderToStaticMarkup( +
+ This concludes the introductory tour of HSS. For more detailed information, + read our User Guide + or visit our support page to revisit the tour or access the Help Portal, where you can report bugs, + request technical assistance, submit feedback, or submit other requests.

+ + Happy searching! +
+ ), + buttons: [ + { + classes: 'shepherd-button-secondary', + text: 'Back', + type: 'back' + }, + { + classes: 'shepherd-button-primary', + text: 'Done', + type: 'next' + } + ], + when: { + show: () => { supportNavDomMask.showMask() }, + hide: () => { supportNavDomMask.hideMask() }, + cancel: () => { supportNavDomMask.hideMask() }, + complete: () => { supportNavDomMask.hideMask() } + } + } ] return [] }, [searchBarDomMask, basePath, navigate, doSearch]) diff --git a/src/hooks/use-synthetic-dom-mask.tsx b/src/hooks/use-synthetic-dom-mask.tsx index 01603fef..948a9034 100644 --- a/src/hooks/use-synthetic-dom-mask.tsx +++ b/src/hooks/use-synthetic-dom-mask.tsx @@ -2,7 +2,7 @@ import { useCallback, useEffect, useMemo, useRef, useState } from 'react' import { v4 as uuid } from 'uuid' interface SyntheticDOMMaskOptions { - padding?: number + padding?: number | { top: number, right: number, bottom: number, left: number } resizeInterval?: number selectorInterval?: number blockClicks?: boolean @@ -45,6 +45,18 @@ export const useSyntheticDOMMask = ( element: mask }) as const, [mask, selector]) + const [paddingTop, paddingRight, paddingBottom, paddingLeft] = useMemo(() => (typeof padding === "number" ? [ + padding, + padding, + padding, + padding + ] : [ + padding.top, + padding.right, + padding.bottom, + padding.left + ]), [padding]) + const resize = useCallback((element: HTMLElement, bb: DOMRect) => { const elBB = element.getBoundingClientRect() if (elBB.x !== bb.x) element.style.left = (bb.x) + "px" @@ -59,13 +71,13 @@ export const useSyntheticDOMMask = ( elements.forEach((element) => { const bb = element.getBoundingClientRect() if (bb.width === 0 || bb.height === 0) return - x1 = Math.min(x1, bb.x - padding) - y1 = Math.min(y1, bb.y - padding) - x2 = Math.max(x2, bb.right + padding) - y2 = Math.max(y2, bb.bottom + padding) + x1 = Math.min(x1, bb.x - paddingLeft) + y1 = Math.min(y1, bb.y - paddingTop) + x2 = Math.max(x2, bb.right + paddingRight) + y2 = Math.max(y2, bb.bottom + paddingBottom) }) return new DOMRect(x1, y1, x2 - x1, y2 - y1) - }, [padding]) + }, [paddingTop, paddingRight, paddingBottom, paddingLeft]) useEffect(() => { mask.style.pointerEvents = blockClicks ? "auto" : "none"