diff --git a/www/segment-constants.js b/www/segment-constants.js index 8b64c1f7f2b..e69de29bb2d 100644 --- a/www/segment-constants.js +++ b/www/segment-constants.js @@ -1,73 +0,0 @@ -/** - * Events are identified by the following structured pattern: - * openedx.paragon.event_environment.event_name.event_action. - * - */ - -export const sendSelectedIconCopy = (context) => { - global.analytics.track('openedx.paragon.docs.icons-table.selected-icon.copied', context); -}; - -export const sendLeaveFeedbackClick = () => { - global.analytics.track('openedx.paragon.docs.leave_feedback.clicked'); -}; - -export const sendPlaygroundClick = () => { - global.analytics.track('openedx.paragon.docs.menu.playground.visit_playground.clicked'); -}; - -export const sendContrastCheckerClick = () => { - global.analytics.track('openedx.paragon.docs.menu.tools.visit_contrast_checker.clicked'); -}; - -export const sendPlaygroundUrlCopy = () => { - global.analytics.track('openedx.paragon.docs.playground.copy-url.copied'); -}; - -export const sendPageEditBtnClick = () => { - global.analytics.track('openedx.paragon.docs.page_edit.clicked'); -}; - -export const sendShadowGeneratorLayerEnabled = () => { - global.analytics.track('openedx.paragon.docs.elevation.shadow-generator.layer.enabled'); -}; - -export const sendShadowGeneratorLayerDisabled = () => { - global.analytics.track('openedx.paragon.docs.elevation.shadow-generator.layer.disabled'); -}; - -export const sendShadowGeneratorLayerRemoved = () => { - global.analytics.track('openedx.paragon.docs.elevation.shadow-generator.layer.removed'); -}; - -export const sendShadowGeneratorLayerAdded = () => { - global.analytics.track('openedx.paragon.docs.elevation.shadow-generator.layer.added'); -}; - -export const sendShadowGeneratorUpdated = (context) => { - global.analytics.track('openedx.paragon.docs.elevation.shadow-generator.updated', context); -}; - -export const sendInsightsTabClick = (tabTitle) => { - global.analytics.track(`openedx.paragon.docs.insights.tabs.${tabTitle.toLowerCase().trim()}.clicked`); -}; - -export const sendComponentUsageLinkClick = (context) => { - global.analytics.track('openedx.paragon.docs.usage-insights.component-usage-link.clicked', context); -}; - -export const sendSettingsVisibility = (isOpen) => { - global.analytics.track(`openedx.paragon.docs.settings.${isOpen ? 'opened' : 'closed'}`); -}; - -export const sendSettingsChange = (settingName, context) => { - global.analytics.track(`openedx.paragon.docs.settings.${settingName}.changed`, context); -}; - -export const sendExampleWithHeading = (collapseIsOpen, context) => { - global.analytics.track(`openedx.paragon.docs.ui.example-code-block.${collapseIsOpen ? 'closed' : 'opened'}`, context); -}; - -export const sendExampleWithoutHeading = (collapseIsOpen, context) => { - global.analytics.track(`openedx.paragon.docs.ui.example-code-block.${collapseIsOpen ? 'closed' : 'opened'}`, context); -}; diff --git a/www/segment-events/constants.js b/www/segment-events/constants.js new file mode 100644 index 00000000000..b7df889900f --- /dev/null +++ b/www/segment-events/constants.js @@ -0,0 +1,26 @@ +/** + * Events are identified by the following structured pattern: + * openedx.paragon.event_environment.event_name.event_action. + */ + +export const ICON_COPIED = 'openedx.paragon.docs.icons-table.selected-icon.copied'; +export const LEAVE_FEEDBACK_CLICKED = 'openedx.paragon.docs.leave_feedback.clicked'; +export const PLAYGROUND_CLICKED = 'openedx.paragon.docs.menu.playground.visit_playground.clicked'; +export const CONTRAST_CHECKER_CLICKED = 'openedx.paragon.docs.menu.tools.visit_contrast_checker.clicked'; +export const PLAYGROUND_URL_COPIED = 'openedx.paragon.docs.playground.copy-url.copied'; +export const PAGE_EDIT_BTN_CLICKED = 'openedx.paragon.docs.page_edit.clicked'; +export const SHADOW_GENERATOR_LAYER_ENABLED = 'openedx.paragon.docs.elevation.shadow-generator.layer.enabled'; +export const SHADOW_GENERATOR_LAYER_DISABLED = 'openedx.paragon.docs.elevation.shadow-generator.layer.disabled'; +export const SHADOW_GENERATOR_LAYER_REMOVED = 'openedx.paragon.docs.elevation.shadow-generator.layer.removed'; +export const SHADOW_GENERATOR_LAYER_ADDED = 'openedx.paragon.docs.elevation.shadow-generator.layer.added'; +export const SHADOW_GENERATOR_LAYER_UPDATED = 'openedx.paragon.docs.elevation.shadow-generator.layer.updated'; +export const COMPONENT_USAGE_LINK_CLICKED = 'openedx.paragon.docs.usage-insights.component-usage-link.clicked'; +export const INSIGHTS_TAB_CLICKED = 'openedx.paragon.docs.insights.tab.clicked'; +export const SETTINGS_OPENED = 'openedx.paragon.docs.settings.opened'; +export const SETTINGS_CLOSED = 'openedx.paragon.docs.settings.closed'; +export const SETTINGS_CHANGED = 'openedx.paragon.docs.setting.changed'; +export const EXAMPLE_CODE_BLOCK_WITH_HEADING_CLOSED = 'openedx.paragon.docs.ui.example-code-block.closed'; +export const EXAMPLE_CODE_BLOCK_WITH_HEADING_OPENED = 'openedx.paragon.docs.ui.example-code-block.opened'; + +export const EXAMPLE_CODE_BLOCK_WITHOUT_HEADING_CLOSED = 'openedx.paragon.docs.ui.example-code-block.without-heading.closed'; +export const EXAMPLE_CODE_BLOCK_WITHOUT_HEADING_OPENED = 'openedx.paragon.docs.ui.example-code-block.without-heading.opened'; diff --git a/www/segment-events/index.js b/www/segment-events/index.js new file mode 100644 index 00000000000..da317444b78 --- /dev/null +++ b/www/segment-events/index.js @@ -0,0 +1,24 @@ +export { + ICON_COPIED, + LEAVE_FEEDBACK_CLICKED, + PLAYGROUND_CLICKED, + CONTRAST_CHECKER_CLICKED, + PLAYGROUND_URL_COPIED, + PAGE_EDIT_BTN_CLICKED, + SHADOW_GENERATOR_LAYER_ENABLED, + SHADOW_GENERATOR_LAYER_DISABLED, + SHADOW_GENERATOR_LAYER_REMOVED, + SHADOW_GENERATOR_LAYER_ADDED, + SHADOW_GENERATOR_LAYER_UPDATED, + COMPONENT_USAGE_LINK_CLICKED, + INSIGHTS_TAB_CLICKED, + SETTINGS_OPENED, + SETTINGS_CLOSED, + SETTINGS_CHANGED, + EXAMPLE_CODE_BLOCK_WITH_HEADING_CLOSED, + EXAMPLE_CODE_BLOCK_WITH_HEADING_OPENED, + EXAMPLE_CODE_BLOCK_WITHOUT_HEADING_CLOSED, + EXAMPLE_CODE_BLOCK_WITHOUT_HEADING_OPENED, +} from './constants'; + +export { sendUserAnalyticsEvent } from './utils'; diff --git a/www/segment-events/utils.js b/www/segment-events/utils.js new file mode 100644 index 00000000000..ae904a2fabe --- /dev/null +++ b/www/segment-events/utils.js @@ -0,0 +1,9 @@ +function sendUserAnalyticsEvent(eventName, context) { + if (context) { + return global.analytics.track(eventName, context); + } + + return global.analytics.track(eventName); +} + +module.exports = { sendUserAnalyticsEvent }; diff --git a/www/src/components/CodeBlock.tsx b/www/src/components/CodeBlock.tsx index dccd01d3d61..0f83f9a3ece 100644 --- a/www/src/components/CodeBlock.tsx +++ b/www/src/components/CodeBlock.tsx @@ -21,7 +21,13 @@ import { FormattedMessage, useIntl } from 'react-intl'; import * as ParagonReact from '~paragon-react'; import * as ParagonIcons from '~paragon-icons'; import { ContentCopy } from '~paragon-icons'; -import { sendExampleWithHeading, sendExampleWithoutHeading } from '../../segment-constants'; +import { + EXAMPLE_CODE_BLOCK_WITH_HEADING_CLOSED, + EXAMPLE_CODE_BLOCK_WITH_HEADING_OPENED, + EXAMPLE_CODE_BLOCK_WITHOUT_HEADING_CLOSED, + EXAMPLE_CODE_BLOCK_WITHOUT_HEADING_OPENED, + sendUserAnalyticsEvent, +} from '../../segment-events'; import MiyazakiCard from './exampleComponents/MiyazakiCard'; import HipsterIpsum from './exampleComponents/HipsterIpsum'; import ExamplePropsForm from './exampleComponents/ExamplePropsForm'; @@ -65,14 +71,16 @@ function CollapsibleLiveEditor({ children, clickToCopy, handleCodeChange }: Coll const headingElement = getCodeBlockHeading(e.target); if (!headingElement) { - sendExampleWithoutHeading(collapseIsOpen, { + sendUserAnalyticsEvent(collapseIsOpen + ? EXAMPLE_CODE_BLOCK_WITHOUT_HEADING_CLOSED : EXAMPLE_CODE_BLOCK_WITHOUT_HEADING_OPENED, { value: `${componentNameAndCategory}id-not-generated`, }); return; } - sendExampleWithHeading(collapseIsOpen, { + sendUserAnalyticsEvent(collapseIsOpen + ? EXAMPLE_CODE_BLOCK_WITH_HEADING_CLOSED : EXAMPLE_CODE_BLOCK_WITH_HEADING_OPENED, { value: `${componentNameAndCategory}${headingElement.id}`, }); }; diff --git a/www/src/components/IconsTable.tsx b/www/src/components/IconsTable.tsx index a8dd492a465..8168d8e52c2 100644 --- a/www/src/components/IconsTable.tsx +++ b/www/src/components/IconsTable.tsx @@ -3,7 +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 { sendSelectedIconCopy } from '../../segment-constants'; +import { ICON_COPIED, sendUserAnalyticsEvent } from '../../segment-events'; const WINDOW_HEIGHT = 2400; const ROW_HEIGHT = 100; @@ -76,7 +76,7 @@ function IconsTable({ iconNames }) { const copyToClipboard = (content) => { navigator.clipboard.writeText(content); setShowToast(true); - sendSelectedIconCopy({ name: currentIcon }); + sendUserAnalyticsEvent(ICON_COPIED, { 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 f9a5b61d7a5..d48a56a5971 100644 --- a/www/src/components/LeaveFeedback.tsx +++ b/www/src/components/LeaveFeedback.tsx @@ -2,7 +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 { sendLeaveFeedbackClick } from '../../segment-constants'; +import { LEAVE_FEEDBACK_CLICKED, sendUserAnalyticsEvent } from '../../segment-events'; export interface LeaveFeedbackProps extends Partial> { isNavLink?: boolean; @@ -13,7 +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 = () => sendLeaveFeedbackClick(); + const handleLinkFeedbackClick = () => sendUserAnalyticsEvent(LEAVE_FEEDBACK_CLICKED); if (isNavLink) { return ( diff --git a/www/src/components/Menu.tsx b/www/src/components/Menu.tsx index e95c90ece93..ec2a7c8542a 100644 --- a/www/src/components/Menu.tsx +++ b/www/src/components/Menu.tsx @@ -16,7 +16,7 @@ import classNames from 'classnames'; import Search from './Search'; import { SettingsContext } from '../context/SettingsContext'; import { THEMES } from '../../theme-config'; -import { sendPlaygroundClick, sendContrastCheckerClick } from '../../segment-constants'; +import { PLAYGROUND_CLICKED, sendUserAnalyticsEvent, CONTRAST_CHECKER_CLICKED } from '../../segment-events'; import { FOUNDATION_PAGES } from '../config'; // MDX transforms markdown generated by gatsby-transformer-react-docgen @@ -146,9 +146,9 @@ function MenuComponentListCategory({ children, title }: IMenuComponentListCatego ); } -const handlePlaygroundClick = () => sendPlaygroundClick(); +const handlePlaygroundClick = () => sendUserAnalyticsEvent(PLAYGROUND_CLICKED); -const handleContrastCheckerClick = () => sendContrastCheckerClick(); +const handleContrastCheckerClick = () => sendUserAnalyticsEvent(CONTRAST_CHECKER_CLICKED); MenuComponentListCategory.propTypes = { children: PropTypes.node.isRequired, diff --git a/www/src/components/PageEditBtn/index.tsx b/www/src/components/PageEditBtn/index.tsx index bc9bee63dc4..f95aa1ddccc 100644 --- a/www/src/components/PageEditBtn/index.tsx +++ b/www/src/components/PageEditBtn/index.tsx @@ -1,7 +1,7 @@ import React, { AnchorHTMLAttributes } from 'react'; import PropTypes from 'prop-types'; import { Button, Hyperlink, Nav } from '~paragon-react'; -import { sendPageEditBtnClick } from '../../../segment-constants'; +import { PAGE_EDIT_BTN_CLICKED, sendUserAnalyticsEvent } from '../../../segment-events'; export interface PageEditBtnProps extends Partial> { githubEditPath?: string, @@ -11,7 +11,7 @@ export interface PageEditBtnProps extends Partial sendPageEditBtnClick(); + const handlePageEditBtnClick = () => sendUserAnalyticsEvent(PAGE_EDIT_BTN_CLICKED); if (isNavLink) { return ( diff --git a/www/src/components/insights/UsagesList.tsx b/www/src/components/insights/UsagesList.tsx index a9b57cf2ca7..52e0673c320 100644 --- a/www/src/components/insights/UsagesList.tsx +++ b/www/src/components/insights/UsagesList.tsx @@ -1,7 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { Hyperlink } from '~paragon-react'; -import { sendComponentUsageLinkClick } from '../../../segment-constants'; +import { COMPONENT_USAGE_LINK_CLICKED, sendUserAnalyticsEvent } from '../../../segment-events'; type UsagesType = { filePath: string, @@ -22,7 +22,9 @@ export default function UsagesList({ projectName, } : UsagesListPropTypes) { const handleUsageLinkClick = (linkToUsage) => { - sendComponentUsageLinkClick({ project: projectName, component: componentName, linkToUsage }); + sendUserAnalyticsEvent(COMPONENT_USAGE_LINK_CLICKED, { + project: projectName, component: componentName, linkToUsage, + }); }; return ( diff --git a/www/src/context/SettingsContext.tsx b/www/src/context/SettingsContext.tsx index 8eee74162b4..e16b8375908 100644 --- a/www/src/context/SettingsContext.tsx +++ b/www/src/context/SettingsContext.tsx @@ -5,7 +5,9 @@ import { IntlProvider } from 'react-intl'; import { messages } from '~paragon-react'; import { THEMES, DEFAULT_THEME } from '../../theme-config'; -import { sendSettingsVisibility, sendSettingsChange } from '../../segment-constants'; +import { + SETTINGS_OPENED, SETTINGS_CLOSED, SETTINGS_CHANGED, sendUserAnalyticsEvent, +} from '../../segment-events'; export interface IDefaultValue { settings: { @@ -45,12 +47,12 @@ function SettingsContextProvider({ children }) { } setSettings(prevState => ({ ...prevState, [key]: value })); global.localStorage.setItem('pgn__settings', JSON.stringify({ ...settings, [key]: value })); - sendSettingsChange(key, { [key]: value }); + sendUserAnalyticsEvent(SETTINGS_CHANGED, { [key]: value }); }; const toggleSettings = (value: boolean) => { setShowSettings(value); - sendSettingsVisibility(); + sendUserAnalyticsEvent(value ? SETTINGS_OPENED : SETTINGS_CLOSED); }; // 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 1491d58ca8c..79d514ee18a 100644 --- a/www/src/pages/foundations/elevation.jsx +++ b/www/src/pages/foundations/elevation.jsx @@ -14,12 +14,13 @@ import SEO from '../../components/SEO'; import Layout from '../../components/PageLayout'; import { SettingsContext } from '../../context/SettingsContext'; import { - sendShadowGeneratorLayerAdded, - sendShadowGeneratorLayerDisabled, - sendShadowGeneratorLayerEnabled, - sendShadowGeneratorLayerRemoved, - sendShadowGeneratorUpdated, -} from '../../../segment-constants'; + sendUserAnalyticsEvent, + SHADOW_GENERATOR_LAYER_DISABLED, + SHADOW_GENERATOR_LAYER_ENABLED, + SHADOW_GENERATOR_LAYER_REMOVED, + SHADOW_GENERATOR_LAYER_ADDED, + SHADOW_GENERATOR_LAYER_UPDATED, +} from '../../../segment-events'; const boxShadowSides = ['down', 'up', 'right', 'left', 'centered']; const boxShadowLevels = [1, 2, 3, 4, 5]; @@ -93,7 +94,7 @@ function BoxShadowToolkit({ }); const updateBoxShadowModel = (property, value) => { - sendShadowGeneratorUpdated({ property, value }); + sendUserAnalyticsEvent(SHADOW_GENERATOR_LAYER_UPDATED, { property, value }); const newBoxShadowModel = { ...boxShadowModel, @@ -203,7 +204,7 @@ function BoxShadowGenerator() { }; const addNewBoxShadowLayer = () => { - sendShadowGeneratorLayerAdded(); + sendUserAnalyticsEvent(SHADOW_GENERATOR_LAYER_ADDED); setBoxShadows([ ...boxShadows, { id: boxShadows[boxShadows.length - 1].id + 1, enabled: true, style: DEFAULT_BOX_SHADOW }, @@ -211,12 +212,12 @@ function BoxShadowGenerator() { }; const removeBoxShadowLayer = (toolkitId) => { - sendShadowGeneratorLayerRemoved(); + sendUserAnalyticsEvent(SHADOW_GENERATOR_LAYER_REMOVED); setBoxShadows(boxShadows.filter((shadow) => shadow.id !== toolkitId)); }; const disableBoxShadowLayer = (toolkitId) => { - sendShadowGeneratorLayerDisabled(); + sendUserAnalyticsEvent(SHADOW_GENERATOR_LAYER_DISABLED); const updatedBoxShadows = boxShadows .map((shadow) => { if (shadow.id === toolkitId) { @@ -228,7 +229,7 @@ function BoxShadowGenerator() { }; const enableBoxShadowLayer = (toolkitId) => { - sendShadowGeneratorLayerEnabled(); + sendUserAnalyticsEvent(SHADOW_GENERATOR_LAYER_ENABLED); const updatedBoxShadows = boxShadows .map((shadow) => { if (shadow.id === toolkitId) { diff --git a/www/src/pages/insights.tsx b/www/src/pages/insights.tsx index 0b69fc2d16e..46b1086fff1 100644 --- a/www/src/pages/insights.tsx +++ b/www/src/pages/insights.tsx @@ -18,7 +18,8 @@ import ComponentsUsage from '../components/insights/ComponentsUsage'; // @ts-ignore import dependentProjectsAnalysis from '../../../dependent-usage.json'; // eslint-disable-line -import { sendInsightsTabClick } from '../../segment-constants'; + +import { sendUserAnalyticsEvent, INSIGHTS_TAB_CLICKED } from '../../segment-events'; import { INSIGHTS_TABS, INSIGHTS_PAGES } from '../config'; import componentsUsage from '../utils/componentsUsage'; import { IInsightsContext } from '../types/types'; @@ -64,7 +65,7 @@ export default function InsightsPage({ pageContext: { tab, githubEditPath } }: I const handleOnSelect = (value: string) => { if (value !== tab) { - sendInsightsTabClick(value); + sendUserAnalyticsEvent(INSIGHTS_TAB_CLICKED, { tabName: 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 c355d06088d..5bb802f1eb0 100644 --- a/www/src/pages/playground.tsx +++ b/www/src/pages/playground.tsx @@ -14,7 +14,7 @@ import localforage from 'localforage'; import SEO from '../components/SEO'; import { SiteTitle } from '../components/header'; import { storageKey } from '../../playroom/constants'; -import { sendPlaygroundUrlCopy } from '../../segment-constants'; +import { PLAYGROUND_URL_COPIED, sendUserAnalyticsEvent } from '../../segment-events'; const FEEDBACK_URL = 'https://github.com/openedx/paragon/issues/new?assignees=&labels=playground&template=feedback_template.md&title=[Playground]'; @@ -74,7 +74,7 @@ export default function Playground({ location }) { onClick={() => { setCopyUrlState('copied'); navigator.clipboard.writeText(location.href); - sendPlaygroundUrlCopy(); + sendUserAnalyticsEvent(PLAYGROUND_URL_COPIED); }} labels={{ default: 'Copy URL',