diff --git a/segment-constants.js b/segment-constants.js new file mode 100644 index 00000000000..2c47a0d276b --- /dev/null +++ b/segment-constants.js @@ -0,0 +1,96 @@ +/** + * Event Tracking Segments and Naming Pattern + * + * This file contains functions for tracking events using a standardized naming pattern + * within the Open edX Paragon documentation. Events are uniquely identified by the + * following structured convention: openedx.paragon.event_environment.event_name.event_action. + * For instance, 'openedx.paragon.docs.leave_feedback.clicked' represents the specific + * event of clicking the 'Leave Feedback' option. + * + * @module EventTracking + */ + +export const trackSelectedIconCopy = (context) => { + console.log('trackSelectedIconCopy', context); + global.analytics.track('openedx.paragon.docs.icons-table.selected-icon.copied', context); +}; + +export const trackLeaveFeedbackClick = () => { + console.log('trackLeaveFeedbackClick'); + global.analytics.track('openedx.paragon.docs.leave_feedback.clicked'); +}; + +export const trackPlaygroundClick = () => { + console.log('trackPlaygroundClick'); + global.analytics.track('openedx.paragon.docs.menu.playground.visit_playground.clicked'); +}; + +export const trackContrastCheckerClick = () => { + console.log('trackContrastCheckerClick'); + global.analytics.track('openedx.paragon.docs.menu.tools.visit_contrast_checker.clicked'); +}; + +export const trackPlaygroundUrlCopy = () => { + console.log('trackPlaygroundUrlCopy'); + global.analytics.track('openedx.paragon.docs.playground.copy-url.copied'); +}; + +export const trackPageEditBtnClick = () => { + console.log('trackPageEditBtnClick'); + global.analytics.track('openedx.paragon.docs.page_edit.clicked'); +}; + +export const trackShadowGeneratorLayerEnabled = () => { + console.log('trackShadowGeneratorLayerEnabled'); + global.analytics.track('openedx.paragon.docs.elevation.shadow-generator.layer.enabled'); +}; + +export const trackShadowGeneratorLayerDisabled = () => { + console.log('trackShadowGeneratorLayerDisabled'); + global.analytics.track('openedx.paragon.docs.elevation.shadow-generator.layer.disabled'); +}; + +export const trackShadowGeneratorLayerRemoved = () => { + console.log('trackShadowGeneratorLayerRemoved'); + global.analytics.track('openedx.paragon.docs.elevation.shadow-generator.layer.removed'); +}; + +export const trackShadowGeneratorLayerAdded = () => { + console.log('trackShadowGeneratorLayerAdded'); + global.analytics.track('openedx.paragon.docs.elevation.shadow-generator.layer.added'); +}; + +export const trackShadowGeneratorUpdated = (context) => { + console.log('trackShadowGeneratorUpdated'); + global.analytics.track('openedx.paragon.docs.elevation.shadow-generator.updated', context); +}; + +export const trackTabClick = (tabTitle) => { + console.log('trackTabClick'); + global.analytics.track(`openedx.paragon.docs.insights.tabs.${tabTitle.toLowerCase().trim()}.clicked`); +}; + +export const trackComponentUsageLinkClick = (context) => { + console.log('trackComponentUsageLinkClick', context); + global.analytics.track('openedx.paragon.docs.usage-insights.component-usage-link.clicked', context); +}; + +export const trackSettingsVisibility = (value) => { + console.log('trackSettingsVisibility'); + global.analytics.track(`openedx.paragon.docs.settings.${value ? 'opened' : 'closed'}`); +}; + +export const trackSettingsChange = (settingName, context) => { + console.log('trackSettingsChange'); + global.analytics.track(`openedx.paragon.docs.settings.${settingName}.changed`, context); +}; + +export const trackExampleWithHeading = (collapseIsOpen, context) => { + console.log('trackExampleWithHeading', context); + global.analytics.track(`openedx.paragon.docs.ui.example-code-block.${collapseIsOpen ? 'closed' : 'opened'}`, context); +}; + +export const trackExampleWithoutHeading = (collapseIsOpen, context) => { + console.log('trackExampleWithoutHeading', context); + global.analytics.track(`openedx.paragon.docs.ui.example-code-block.${collapseIsOpen ? 'closed' : 'opened'}`, context); +}; diff --git a/www/src/components/CodeBlock.tsx b/www/src/components/CodeBlock.tsx index b63c3e408aa..839a898d91b 100644 --- a/www/src/components/CodeBlock.tsx +++ b/www/src/components/CodeBlock.tsx @@ -21,6 +21,7 @@ import { FormattedMessage, useIntl } from 'react-intl'; import * as ParagonReact from '~paragon-react'; import * as ParagonIcons from '~paragon-icons'; import { ContentCopy } from '~paragon-icons'; +import { trackExampleWithHeading, trackExampleWithoutHeading } from '../../../segment-constants'; import MiyazakiCard from './exampleComponents/MiyazakiCard'; import HipsterIpsum from './exampleComponents/HipsterIpsum'; import ExamplePropsForm from './exampleComponents/ExamplePropsForm'; @@ -64,14 +65,14 @@ function CollapsibleLiveEditor({ children, clickToCopy, handleCodeChange }: Coll const headingElement = getCodeBlockHeading(e.target); if (!headingElement) { - global.analytics.track(`openedx.paragon.docs.ui.example-code-block.${collapseIsOpen ? 'closed' : 'opened'}`, { + trackExampleWithoutHeading(collapseIsOpen, { value: `${componentNameAndCategory}id-not-generated`, }); return; } - global.analytics.track(`openedx.paragon.docs.ui.example-code-block.${collapseIsOpen ? 'closed' : 'opened'}`, { + trackExampleWithHeading(collapseIsOpen, { value: `${componentNameAndCategory}${headingElement.id}`, }); }; diff --git a/www/src/components/IconsTable.tsx b/www/src/components/IconsTable.tsx index a3f6a2a8c7e..bb0ba9d8e24 100644 --- a/www/src/components/IconsTable.tsx +++ b/www/src/components/IconsTable.tsx @@ -3,6 +3,7 @@ import PropTypes from 'prop-types'; import debounce from 'lodash.debounce'; import { Icon, SearchField, Toast } from '~paragon-react'; import * as IconComponents from '~paragon-icons'; +import { trackSelectedIconCopy } from '../../../segment-constants'; const WINDOW_HEIGHT = 2400; const ROW_HEIGHT = 100; @@ -75,7 +76,7 @@ function IconsTable({ iconNames }) { const copyToClipboard = (content) => { navigator.clipboard.writeText(content); setShowToast(true); - global.analytics.track('openedx.paragon.docs.icons-table.selected-icon.copied', { name: currentIcon }); + trackSelectedIconCopy({ name: currentIcon }); }; const handleChangeSearchValue = useMemo(() => debounce(setSearchValue, 500, { leading: false }), []); diff --git a/www/src/components/LeaveFeedback.tsx b/www/src/components/LeaveFeedback.tsx index de42cb91331..b7baf9a3c87 100644 --- a/www/src/components/LeaveFeedback.tsx +++ b/www/src/components/LeaveFeedback.tsx @@ -2,6 +2,7 @@ import React, { AnchorHTMLAttributes } from 'react'; import PropTypes from 'prop-types'; import { Nav, Button, Hyperlink } from '~paragon-react'; import { useLocation } from '@reach/router'; +import { trackLeaveFeedbackClick } from '../../../segment-constants'; export interface LeaveFeedbackProps extends Partial> { isNavLink?: boolean; @@ -12,9 +13,7 @@ function LeaveFeedback({ isNavLink, ...props }: LeaveFeedbackProps) { const FEEDBACK_URL = `https://github.com/openedx/paragon/issues/new?title=%5Bparagon-openedx.netlify.app%5D%20Feedback%20(on%20${location.pathname})&labels=docs&template=feedback_template.md`; const leaveFeedbackLinkTitle = 'Leave feedback'; - const handleLinkFeedbackClick = () => { - global.analytics.track('openedx.paragon.docs.leave_feedback.clicked'); - }; + const handleLinkFeedbackClick = () => trackLeaveFeedbackClick(); if (isNavLink) { return ( diff --git a/www/src/components/Menu.tsx b/www/src/components/Menu.tsx index 74afa7a0b3d..c5d92110612 100644 --- a/www/src/components/Menu.tsx +++ b/www/src/components/Menu.tsx @@ -17,6 +17,7 @@ import Search from './Search'; import { SettingsContext } from '../context/SettingsContext'; import { THEMES } from '../../theme-config'; import { FOUNDATION_PAGES } from '../config'; +import { trackPlaygroundClick, trackContrastCheckerClick } from '../../../segment-constants'; // MDX transforms markdown generated by gatsby-transformer-react-docgen // This query filters out all of those markdown nodes and assumes all others @@ -145,13 +146,9 @@ function MenuComponentListCategory({ children, title }: IMenuComponentListCatego ); } -const handlePlaygroundClick = () => { - global.analytics.track('openedx.paragon.docs.menu.playground.visit_playground.clicked'); -}; +const handlePlaygroundClick = () => trackPlaygroundClick(); -const handleContrastCheckerClick = () => { - global.analytics.track('openedx.paragon.docs.menu.tools.visit_contrast_checker.clicked'); -}; +const handleContrastCheckerClick = () => trackContrastCheckerClick(); MenuComponentListCategory.propTypes = { children: PropTypes.node.isRequired, diff --git a/www/src/components/PageEditBtn/index.tsx b/www/src/components/PageEditBtn/index.tsx index 39821143fd0..ef437ac6d41 100644 --- a/www/src/components/PageEditBtn/index.tsx +++ b/www/src/components/PageEditBtn/index.tsx @@ -1,6 +1,7 @@ import React, { AnchorHTMLAttributes } from 'react'; import PropTypes from 'prop-types'; import { Button, Hyperlink, Nav } from '~paragon-react'; +import { trackPageEditBtnClick } from '../../../../segment-constants'; export interface PageEditBtnProps extends Partial> { githubEditPath?: string, @@ -10,9 +11,7 @@ export interface PageEditBtnProps extends Partial { - global.analytics.track('openedx.paragon.docs.page_edit.clicked'); - }; + const handlePageEditBtnClick = () => trackPageEditBtnClick(); if (isNavLink) { return ( diff --git a/www/src/components/insights/UsagesList.tsx b/www/src/components/insights/UsagesList.tsx index b3a5115d2ef..eb143e4069e 100644 --- a/www/src/components/insights/UsagesList.tsx +++ b/www/src/components/insights/UsagesList.tsx @@ -1,6 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { Hyperlink } from '~paragon-react'; +import { trackComponentUsageLinkClick } from '../../../../segment-constants'; type UsagesType = { filePath: string, @@ -21,10 +22,7 @@ export default function UsagesList({ projectName, } : UsagesListPropTypes) { const handleUsageLinkClick = (linkToUsage) => { - global.analytics.track( - 'openedx.paragon.docs.usage-insights.component-usage-link.clicked', - { project: projectName, component: componentName, linkToUsage }, - ); + trackComponentUsageLinkClick({ project: projectName, component: componentName, linkToUsage }); }; return ( diff --git a/www/src/context/SettingsContext.tsx b/www/src/context/SettingsContext.tsx index d902d452fec..527125028d6 100644 --- a/www/src/context/SettingsContext.tsx +++ b/www/src/context/SettingsContext.tsx @@ -5,6 +5,7 @@ import { IntlProvider } from 'react-intl'; import { messages } from '~paragon-react'; import { THEMES, DEFAULT_THEME } from '../../theme-config'; +import { trackSettingsVisibility, trackSettingsChange } from '../../../segment-constants'; export interface IDefaultValue { settings: { @@ -44,12 +45,12 @@ function SettingsContextProvider({ children }) { } setSettings(prevState => ({ ...prevState, [key]: value })); global.localStorage.setItem('pgn__settings', JSON.stringify({ ...settings, [key]: value })); - global.analytics.track(`openedx.paragon.docs.settings.${key}.changed`, { [key]: value }); + trackSettingsChange(key, { [key]: value }); }; const toggleSettings = (value: boolean) => { setShowSettings(value); - global.analytics.track(`openedx.paragon.docs.settings.${value ? 'opened' : 'closed'}`); + trackSettingsVisibility(); }; // this hook will be called after the first render, so we can safely access localStorage diff --git a/www/src/pages/foundations/elevation.jsx b/www/src/pages/foundations/elevation.jsx index 68d1421c568..76813f99c6c 100644 --- a/www/src/pages/foundations/elevation.jsx +++ b/www/src/pages/foundations/elevation.jsx @@ -13,6 +13,13 @@ import { Close, WbSunny, DoDisturb } from '~paragon-icons'; import SEO from '../../components/SEO'; import Layout from '../../components/PageLayout'; import { SettingsContext } from '../../context/SettingsContext'; +import { + trackShadowGeneratorLayerAdded, + trackShadowGeneratorLayerDisabled, + trackShadowGeneratorLayerEnabled, + trackShadowGeneratorLayerRemoved, + trackShadowGeneratorUpdated, +} from "../../../../segment-constants"; const boxShadowSides = ['down', 'up', 'right', 'left', 'centered']; const boxShadowLevels = [1, 2, 3, 4, 5]; @@ -86,7 +93,7 @@ function BoxShadowToolkit({ }); const updateBoxShadowModel = (property, value) => { - global.analytics.track('openedx.paragon.docs.elevation.generator.updated', { property, value }); + trackShadowGeneratorUpdated({ property, value }); const newBoxShadowModel = { ...boxShadowModel, @@ -196,7 +203,7 @@ function BoxShadowGenerator() { }; const addNewBoxShadowLayer = () => { - global.analytics.track('openedx.paragon.elevation.generator.layer.added'); + trackShadowGeneratorLayerAdded(); setBoxShadows([ ...boxShadows, { id: boxShadows[boxShadows.length - 1].id + 1, enabled: true, style: DEFAULT_BOX_SHADOW }, @@ -204,12 +211,12 @@ function BoxShadowGenerator() { }; const removeBoxShadowLayer = (toolkitId) => { - global.analytics.track('openedx.paragon.elevation.shadow-generator.layer.removed'); + trackShadowGeneratorLayerRemoved(); setBoxShadows(boxShadows.filter((shadow) => shadow.id !== toolkitId)); }; const disableBoxShadowLayer = (toolkitId) => { - global.analytics.track('openedx.paragon.elevation.shadow-generator.layer.disabled'); + trackShadowGeneratorLayerDisabled(); const updatedBoxShadows = boxShadows .map((shadow) => { if (shadow.id === toolkitId) { @@ -221,7 +228,7 @@ function BoxShadowGenerator() { }; const enableBoxShadowLayer = (toolkitId) => { - global.analytics.track('openedx.paragon.elevation.shadow-generator.layer.enabled'); + trackShadowGeneratorLayerEnabled(); const updatedBoxShadows = boxShadows .map((shadow) => { if (shadow.id === toolkitId) { diff --git a/www/src/pages/insights.tsx b/www/src/pages/insights.tsx index a2d8626c894..da6dba81723 100644 --- a/www/src/pages/insights.tsx +++ b/www/src/pages/insights.tsx @@ -15,6 +15,7 @@ import HooksUsage from '../components/insights/HooksUsage'; import UtilsUsage from '../components/insights/UtilsUsage'; import IconsUsage from '../components/insights/IconsUsage'; import ComponentsUsage from '../components/insights/ComponentsUsage'; +import { trackTabClick } from '../../../segment-constants'; // @ts-ignore import dependentProjectsAnalysis from '../../../dependent-usage.json'; // eslint-disable-line @@ -63,7 +64,7 @@ export default function InsightsPage({ pageContext: { tab, githubEditPath } }: I const handleOnSelect = (value: string) => { if (value !== tab) { - global.analytics.track(`openedx.paragon.docs.insights.tabs.${value.toLowerCase().trim()}.clicked`); + trackTabClick(value); navigate(INSIGHTS_PAGES.find(item => item.tab === value).path); } }; diff --git a/www/src/pages/playground.tsx b/www/src/pages/playground.tsx index 9707676a47d..57ceafdf2ff 100644 --- a/www/src/pages/playground.tsx +++ b/www/src/pages/playground.tsx @@ -14,6 +14,7 @@ import localforage from 'localforage'; import SEO from '../components/SEO'; import { SiteTitle } from '../components/header'; import { storageKey } from '../../playroom/constants'; +import { trackPlaygroundUrlCopy } from '../../../segment-constants'; const FEEDBACK_URL = 'https://github.com/openedx/paragon/issues/new?assignees=&labels=playground&template=feedback_template.md&title=[Playground]'; @@ -73,7 +74,7 @@ export default function Playground({ location }) { onClick={() => { setCopyUrlState('copied'); navigator.clipboard.writeText(location.href); - global.analytics.track('openedx.paragon.docs.playground.url-copied'); + trackPlaygroundUrlCopy(); }} labels={{ default: 'Copy URL',