diff --git a/src/components/search/concept-card/concept-card.js b/src/components/search/concept-card/concept-card.js index 3eb34fad..2997bf58 100644 --- a/src/components/search/concept-card/concept-card.js +++ b/src/components/search/concept-card/concept-card.js @@ -1,4 +1,4 @@ -import { Fragment, useState, useEffect, useMemo, forwardRef } from 'react' +import { Fragment, useState, useEffect, useMemo, forwardRef, useCallback } from 'react' import PropTypes from 'prop-types' import { Badge, Card, Space, Typography } from 'antd' import { ExpandOutlined as ViewIcon, LoadingOutlined } from '@ant-design/icons' @@ -13,10 +13,17 @@ import './concept-card.css' const { Text } = Typography -export const ConceptCard = forwardRef(({ index, result, openModalHandler, icon=ViewIcon, className="" }, ref) => { +export const ConceptCard = forwardRef(({ + index, + result, + resultRank, + openModalHandler, + icon=ViewIcon, + className="" +}, ref) => { let { name, type } = result - const [currentTab, setCurrentTab] = useState('overview') + const [currentTab, _setCurrentTab] = useState('overview') const [studies, setStudies] = useState(null) const [cdeStudies, setCdeStudies] = useState(null) @@ -56,9 +63,25 @@ export const ConceptCard = forwardRef(({ index, result, openModalHandler, icon=V const tabList = Object.keys(tabs).map(key => tabs[key].content ? ({ key, tab: tabs[key].title }) : null).filter(tab => tab !== null) const tabContents = Object.keys(tabs).reduce((obj, key) => tabs[key].content ? ({ ...obj, [key]: tabs[key].content }) : obj, {}) + const setCurrentTab = useCallback((() => { + let oldTime = Date.now(); + return (tabName) => { + const newTime = Date.now(); + const elapsed = newTime - oldTime; + _setCurrentTab((currentTab) => { + if (tabName !== currentTab) { + // Make sure we only track events when the tab actually changes. + analyticsEvents.resultCardTabSelected(tabs[tabName].title, tabs[currentTab].title, resultRank, elapsed) + } + return tabName + }) + oldTime = newTime; + } + })(), [tabs]) + const openModal = (...args) => { openModalHandler(...args) - analyticsEvents.resultModalOpened(query, result) + analyticsEvents.resultModalOpened(query, result, resultRank) } useEffect(() => { diff --git a/src/components/search/concept-modal/concept-modal.js b/src/components/search/concept-modal/concept-modal.js index 637f12a8..287c1e71 100644 --- a/src/components/search/concept-modal/concept-modal.js +++ b/src/components/search/concept-modal/concept-modal.js @@ -121,7 +121,7 @@ export const ConceptModalBody = ({ result }) => { _setCurrentTab((currentTab) => { if (tabName !== currentTab) { // Make sure we only track events when the tab actually changes. - analyticsEvents.resultTabSelected(tabs[tabName].title, tabs[currentTab].title, elapsed) + analyticsEvents.resultModalTabSelected(tabs[tabName].title, tabs[currentTab].title, elapsed) } return tabName }) diff --git a/src/components/search/results/concepts-grid-layout/concept-search-results.js b/src/components/search/results/concepts-grid-layout/concept-search-results.js index 0f53cf3d..caeb2e3f 100644 --- a/src/components/search/results/concepts-grid-layout/concept-search-results.js +++ b/src/components/search/results/concepts-grid-layout/concept-search-results.js @@ -51,6 +51,7 @@ export const ConceptSearchResults = () => { key={ result.id } index={ index } result={ result } + resultRank={ concepts.indexOf(result) + 1 } openModalHandler={ () => setSelectedResult(result) } /> ) diff --git a/src/components/search/results/expanded-results-layout/expanded-results-sidebar.js b/src/components/search/results/expanded-results-layout/expanded-results-sidebar.js index e1ff4b4f..6d8cb48f 100644 --- a/src/components/search/results/expanded-results-layout/expanded-results-sidebar.js +++ b/src/components/search/results/expanded-results-layout/expanded-results-sidebar.js @@ -92,6 +92,7 @@ export const ExpandedResultsSidebar = ({ expanded, setExpanded }) => { key={result.id} className={classNames("expanded-result-option-concept-card", result.id === selectedResult?.id && "selected")} result={result} + resultRank={ concepts.indexOf(result) + 1 } icon={result.id === selectedResult?.id ? null : ArrowRightOutlined} openModalHandler={ () => setSelectedResult(result) } ref={(ref) => cardRefs.current[result.id] = ref} diff --git a/src/contexts/analytics-context/events/search-events.js b/src/contexts/analytics-context/events/search-events.js index 79986140..baa943ed 100644 --- a/src/contexts/analytics-context/events/search-events.js +++ b/src/contexts/analytics-context/events/search-events.js @@ -12,7 +12,7 @@ export function searchExecuted(query, execTime, resultCount, error=null) { } }); } -export function resultModalOpened(query, result) { +export function resultModalOpened(query, result, resultRank) { this.analytics.trackEvent({ category: "ui_interaction", action: "result_modal_opened", @@ -21,11 +21,26 @@ export function resultModalOpened(query, result) { "search_query": query, "result_name": result.name, "result_type": result.type, + // Rank = index in results sorted by score + 1 + "result_rank": resultRank, "additional_search_terms": result.search_terms } }); } -export function resultTabSelected(newTabTitle, oldTabTitle, elapsed) { +export function resultCardTabSelected(newTabTitle, oldTabTitle, resultRank, elapsed) { + this.analytics.trackEvent({ + category: "ui_interaction", + action: "result_card_tab_selected", + label: newTabTitle, + customParameters: { + "tab_name": newTabTitle, + "previous_tab_name": oldTabTitle, + "result_rank": resultRank, + "time_spent_on_previous_tab": elapsed + } + }); +} +export function resultModalTabSelected(newTabTitle, oldTabTitle, elapsed) { this.analytics.trackEvent({ category: "ui_interaction", action: "result_tab_selected",