@redocly/theme 0.67.0-next.4 → 0.67.0-next.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/lib/components/Breadcrumbs/BreadcrumbDropdown.js +2 -6
  2. package/lib/components/Breadcrumbs/Breadcrumbs.js +3 -18
  3. package/lib/components/Buttons/AIAssistantButton.js +2 -6
  4. package/lib/components/Buttons/EditPageButton.js +2 -1
  5. package/lib/components/Catalog/CatalogCardView/CatalogCard.js +1 -1
  6. package/lib/components/CatalogClassic/CatalogClassicActions.js +3 -1
  7. package/lib/components/CatalogClassic/CatalogClassicCard.js +1 -1
  8. package/lib/components/CatalogClassic/CatalogClassicInfoBlock.js +2 -1
  9. package/lib/components/Feedback/ReportDialog.js +4 -1
  10. package/lib/components/Filter/FilterCheckboxes.js +3 -1
  11. package/lib/components/Footer/FooterItem.js +1 -1
  12. package/lib/components/LanguagePicker/LanguagePicker.js +3 -1
  13. package/lib/components/Logo/Logo.js +2 -1
  14. package/lib/components/Menu/MenuContainer.js +1 -0
  15. package/lib/components/Menu/MenuItem.js +21 -7
  16. package/lib/components/Navbar/Navbar.js +6 -2
  17. package/lib/components/Navbar/NavbarItem.js +3 -1
  18. package/lib/components/Search/SearchDialog.js +5 -33
  19. package/lib/components/Search/SearchInput.js +4 -1
  20. package/lib/components/Search/SearchRecent.js +3 -1
  21. package/lib/components/SidebarActions/SidebarActions.js +10 -3
  22. package/lib/components/TableOfContent/TableOfContent.js +1 -1
  23. package/lib/components/UserMenu/LoginButton.js +2 -1
  24. package/lib/components/UserMenu/LogoutMenuItem.js +2 -1
  25. package/lib/core/hooks/search/use-search-dialog.js +7 -2
  26. package/lib/core/hooks/use-banner-telemetry.js +2 -11
  27. package/lib/core/hooks/use-color-switcher.js +2 -1
  28. package/lib/core/hooks/use-page-actions.js +12 -21
  29. package/lib/core/hooks/use-product-picker.js +14 -4
  30. package/lib/core/hooks/use-telemetry-fallback.d.ts +45 -37
  31. package/lib/core/hooks/use-telemetry-fallback.js +47 -39
  32. package/lib/core/utils/index.d.ts +2 -0
  33. package/lib/core/utils/index.js +2 -0
  34. package/lib/core/utils/telemetry/generate-before-after-context.d.ts +37 -0
  35. package/lib/core/utils/telemetry/generate-before-after-context.js +32 -0
  36. package/lib/core/utils/telemetry/generate-resource-urn.d.ts +7 -0
  37. package/lib/core/utils/telemetry/generate-resource-urn.js +13 -0
  38. package/lib/core/utils/telemetry/get-base-data-attributes.d.ts +14 -0
  39. package/lib/core/utils/telemetry/get-base-data-attributes.js +17 -0
  40. package/package.json +3 -3
  41. package/src/components/Breadcrumbs/BreadcrumbDropdown.tsx +2 -1
  42. package/src/components/Breadcrumbs/Breadcrumbs.tsx +4 -4
  43. package/src/components/Buttons/AIAssistantButton.tsx +2 -3
  44. package/src/components/Buttons/EditPageButton.tsx +4 -1
  45. package/src/components/Catalog/CatalogCardView/CatalogCard.tsx +2 -2
  46. package/src/components/CatalogClassic/CatalogClassicActions.tsx +4 -2
  47. package/src/components/CatalogClassic/CatalogClassicCard.tsx +9 -2
  48. package/src/components/CatalogClassic/CatalogClassicInfoBlock.tsx +2 -1
  49. package/src/components/Feedback/ReportDialog.tsx +4 -1
  50. package/src/components/Filter/FilterCheckboxes.tsx +4 -2
  51. package/src/components/Footer/FooterItem.tsx +4 -2
  52. package/src/components/LanguagePicker/LanguagePicker.tsx +9 -2
  53. package/src/components/Logo/Logo.tsx +7 -1
  54. package/src/components/Menu/MenuContainer.tsx +1 -0
  55. package/src/components/Menu/MenuItem.tsx +35 -4
  56. package/src/components/Navbar/Navbar.tsx +7 -3
  57. package/src/components/Navbar/NavbarItem.tsx +7 -1
  58. package/src/components/Search/SearchDialog.tsx +6 -13
  59. package/src/components/Search/SearchInput.tsx +4 -1
  60. package/src/components/Search/SearchRecent.tsx +4 -2
  61. package/src/components/SidebarActions/SidebarActions.tsx +10 -3
  62. package/src/components/TableOfContent/TableOfContent.tsx +7 -2
  63. package/src/components/UserMenu/LoginButton.tsx +4 -1
  64. package/src/components/UserMenu/LogoutMenuItem.tsx +2 -1
  65. package/src/core/hooks/search/use-search-dialog.ts +13 -2
  66. package/src/core/hooks/use-banner-telemetry.ts +5 -4
  67. package/src/core/hooks/use-color-switcher.ts +7 -1
  68. package/src/core/hooks/use-page-actions.ts +17 -28
  69. package/src/core/hooks/use-product-picker.ts +19 -5
  70. package/src/core/hooks/use-telemetry-fallback.ts +47 -39
  71. package/src/core/utils/index.ts +2 -0
  72. package/src/core/utils/telemetry/generate-before-after-context.ts +59 -0
  73. package/src/core/utils/telemetry/generate-resource-urn.ts +9 -0
  74. package/src/core/utils/telemetry/get-base-data-attributes.ts +27 -0
@@ -4,7 +4,7 @@ import styled from 'styled-components';
4
4
  import type { JSX } from 'react';
5
5
  import type { ResolvedFilter } from '@redocly/theme/core/types';
6
6
 
7
- import { breakpoints } from '@redocly/theme/core/utils';
7
+ import { breakpoints, getBaseDataAttributes } from '@redocly/theme/core/utils';
8
8
  import { useThemeHooks } from '@redocly/theme/core/hooks';
9
9
  import { Button } from '@redocly/theme/components/Button/Button';
10
10
  import { FilterIcon } from '@redocly/theme/icons/FilterIcon/FilterIcon';
@@ -42,7 +42,9 @@ export function CatalogClassicActions(props: CatalogClassicActionsProps): JSX.El
42
42
  iconPosition="left"
43
43
  onClick={() => {
44
44
  onOpenFilter();
45
- telemetry.sendCatalogActionsButtonClickedMessage();
45
+ telemetry.sendCatalogActionsClickedMessage([
46
+ getBaseDataAttributes('catalogActions', 'button'),
47
+ ]);
46
48
  }}
47
49
  data-translation-key="catalog.filters.title"
48
50
  >
@@ -4,7 +4,12 @@ import styled from 'styled-components';
4
4
  import type { JSX } from 'react';
5
5
  import type { CatalogItem } from '@redocly/theme/core/types';
6
6
 
7
- import { slug, capitalize, getScorecardColorVariable } from '@redocly/theme/core/utils';
7
+ import {
8
+ slug,
9
+ capitalize,
10
+ getScorecardColorVariable,
11
+ getBaseDataAttributes,
12
+ } from '@redocly/theme/core/utils';
8
13
  import { useThemeHooks } from '@redocly/theme/core/hooks';
9
14
  import { ArrowRightIcon } from '@redocly/theme/icons/ArrowRightIcon/ArrowRightIcon';
10
15
  import { Link } from '@redocly/theme/components/Link/Link';
@@ -38,7 +43,9 @@ export function CatalogClassicCard({ item }: CatalogClassicCardProps): JSX.Eleme
38
43
  <Link key={item.docsLink || item.link} to={item.docsLink || item.link}>
39
44
  <StyledCard
40
45
  data-component-name="CatalogClassic/CatalogClassicCard"
41
- onClick={() => telemetry.sendCatalogItemClickedMessage()}
46
+ onClick={() =>
47
+ telemetry.sendCatalogItemClickedMessage([getBaseDataAttributes('catalogItem', 'card')])
48
+ }
42
49
  >
43
50
  <CardContent>
44
51
  <CardTitleWrapper>
@@ -7,6 +7,7 @@ import { useThemeHooks } from '@redocly/theme/core/hooks';
7
7
  import { getScorecardColorVariable } from '@redocly/theme/core/utils';
8
8
  import { Tag } from '@redocly/theme/components/Tag/Tag';
9
9
  import { Link } from '@redocly/theme/components/Link/Link';
10
+ import { getBaseDataAttributes } from '@redocly/theme/core/utils';
10
11
 
11
12
  export type CatalogClassicInfoBlockProps = {
12
13
  metadata?: {
@@ -53,7 +54,7 @@ function ScorecardBadge(props: {
53
54
  <Link to={slug}>
54
55
  <Tag
55
56
  onClick={() =>
56
- telemetry.sendScorecardLinkClickedMessage([{ object: 'link', action: 'click' }])
57
+ telemetry.sendScorecardLinkClickedMessage([getBaseDataAttributes('scorecardLink', 'tag')])
57
58
  }
58
59
  withStatusDot
59
60
  statusDotColor={`var(${colorVariable})`}
@@ -8,6 +8,7 @@ import type { SubmitFeedbackParams } from '@redocly/theme/core/types';
8
8
  import { useThemeHooks } from '@redocly/theme/core/hooks';
9
9
  import { Comment } from '@redocly/theme/components/Feedback/Comment';
10
10
  import { Portal } from '@redocly/theme/components/Portal/Portal';
11
+ import { getBaseDataAttributes } from '@redocly/theme/core/utils';
11
12
 
12
13
  export type ReportDialogProps = {
13
14
  location: string;
@@ -47,7 +48,9 @@ export function ReportDialog({
47
48
  path: pathname,
48
49
  lang,
49
50
  });
50
- telemetry.sendCodeSnippetReportedMessage();
51
+ telemetry.sendCodeSnippetReportedMessage([
52
+ getBaseDataAttributes('codeSnippet', 'markdocTag'),
53
+ ]);
51
54
  onSubmit();
52
55
  }}
53
56
  isDialog={true}
@@ -11,7 +11,7 @@ import { FilterOptionLabel } from '@redocly/theme/components/Filter/FilterOption
11
11
  import { useThemeHooks } from '@redocly/theme/core/hooks';
12
12
  import { CheckboxIcon } from '@redocly/theme/icons/CheckboxIcon/CheckboxIcon';
13
13
  import { CounterTag } from '@redocly/theme/components/Tags/CounterTag';
14
- import { changeTextCasing } from '@redocly/theme/core/utils';
14
+ import { changeTextCasing, getBaseDataAttributes } from '@redocly/theme/core/utils';
15
15
 
16
16
  export function FilterCheckboxes({
17
17
  filter,
@@ -36,7 +36,9 @@ export function FilterCheckboxes({
36
36
  role="link"
37
37
  onClick={() => {
38
38
  filter.toggleOption(value);
39
- telemetry.sendFilterCheckboxToggledMessage([{ object: 'checkbox', id }]);
39
+ telemetry.sendFilterCheckboxToggledMessage([
40
+ getBaseDataAttributes('filterId', 'checkbox'),
41
+ ]);
40
42
  }}
41
43
  >
42
44
  <CheckboxIcon
@@ -7,7 +7,7 @@ import type { ResolvedNavItem } from '@redocly/config';
7
7
  import { useThemeHooks } from '@redocly/theme/core/hooks';
8
8
  import { LaunchIcon } from '@redocly/theme/icons/LaunchIcon/LaunchIcon';
9
9
  import { Link } from '@redocly/theme/components/Link/Link';
10
- import { breakpoints } from '@redocly/theme/core/utils';
10
+ import { breakpoints, getBaseDataAttributes } from '@redocly/theme/core/utils';
11
11
  import { GenericIcon } from '@redocly/theme/icons/GenericIcon/GenericIcon';
12
12
 
13
13
  export type FooterItemProps = {
@@ -44,7 +44,9 @@ export function FooterItem({ item, iconsOnly, className }: FooterItemProps): JSX
44
44
  external={item.external}
45
45
  target={item.target}
46
46
  data-testid={item.label}
47
- onClick={() => telemetry.sendFooterItemClickedMessage()}
47
+ onClick={() =>
48
+ telemetry.sendFooterItemClickedMessage([getBaseDataAttributes('footerItem', 'footer')])
49
+ }
48
50
  data-translation-key={item.labelTranslationKey}
49
51
  >
50
52
  {hasIcon ? (
@@ -4,7 +4,7 @@ import styled from 'styled-components';
4
4
  import type { JSX } from 'react';
5
5
 
6
6
  import { DropdownMenu } from '@redocly/theme/components/Dropdown/DropdownMenu';
7
- import { breakpoints } from '@redocly/theme/core/utils';
7
+ import { breakpoints, generateBeforeAfterContext } from '@redocly/theme/core/utils';
8
8
  import { useLanguagePicker, useThemeHooks } from '@redocly/theme/core/hooks';
9
9
  import { GlobalOutlinedIcon } from '@redocly/theme/icons/GlobalOutlinedIcon/GlobalOutlinedIcon';
10
10
  import { Button } from '@redocly/theme/components/Button/Button';
@@ -41,7 +41,14 @@ export function LanguagePicker(props: LanguagePickerProps): JSX.Element | null {
41
41
  languageInsensitive: true,
42
42
  onAction: () => {
43
43
  props.onChangeLanguage(locale.code);
44
- telemetry.sendLanguagePickerLocaleChangedMessage([{ object: 'locale', locale: locale.code }]);
44
+ telemetry.sendLanguagePickerChangedMessage([
45
+ ...generateBeforeAfterContext<'sendLanguagePickerChangedMessage'>(
46
+ 'languagePicker',
47
+ 'dropdown',
48
+ { locale: currentLocale.code },
49
+ { locale: locale.code },
50
+ ),
51
+ ]);
45
52
  },
46
53
  active: locale.code === currentLocale.code,
47
54
  suffix: locale.code === currentLocale.code && <CheckmarkIcon />,
@@ -7,6 +7,7 @@ import type { LogoConfig } from '@redocly/theme/core/types';
7
7
  import { useThemeHooks } from '@redocly/theme/core/hooks';
8
8
  import { Link } from '@redocly/theme/components/Link/Link';
9
9
  import { Image } from '@redocly/theme/components/Image/Image';
10
+ import { getBaseDataAttributes } from '@redocly/theme/core/utils';
10
11
 
11
12
  export type LogoProps = {
12
13
  config: LogoConfig;
@@ -27,7 +28,12 @@ export function Logo({ config, className, ...otherProps }: LogoProps): JSX.Eleme
27
28
  return (
28
29
  <LogoWrapper data-component-name="Logo/Logo" className={className} {...otherProps}>
29
30
  {config.link ? (
30
- <Link to={config.link} onClick={() => telemetry.sendLogoClickedMessage()}>
31
+ <Link
32
+ to={config.link}
33
+ onClick={() =>
34
+ telemetry.sendLogoClickedMessage([getBaseDataAttributes('logo', 'navbar')])
35
+ }
36
+ >
31
37
  {image}
32
38
  </Link>
33
39
  ) : (
@@ -67,6 +67,7 @@ const MenuContainerComponent = styled.div.attrs<{
67
67
  animation-timing-function: ease;
68
68
  position: relative;
69
69
  overflow-y: auto;
70
+ scrollbar-gutter: stable;
70
71
  flex-grow: ${({ $growContent }) => ($growContent ? 1 : 0)};
71
72
  padding-top: var(--menu-container-padding-top);
72
73
  display: ${({ $hidden }) => ($hidden ? 'none' : 'block')};
@@ -11,7 +11,11 @@ import { ChevronDownIcon } from '@redocly/theme/icons/ChevronDownIcon/ChevronDow
11
11
  import { ChevronRightIcon } from '@redocly/theme/icons/ChevronRightIcon/ChevronRightIcon';
12
12
  import { HttpTag } from '@redocly/theme/components/Tags/HttpTag';
13
13
  import { MenuItemType } from '@redocly/theme/core/constants';
14
- import { getMenuItemType, getOperationColor } from '@redocly/theme/core/utils';
14
+ import {
15
+ getBaseDataAttributes,
16
+ getMenuItemType,
17
+ getOperationColor,
18
+ } from '@redocly/theme/core/utils';
15
19
  import { ArrowRightIcon } from '@redocly/theme/icons/ArrowRightIcon/ArrowRightIcon';
16
20
  import { GenericIcon } from '@redocly/theme/icons/GenericIcon/GenericIcon';
17
21
  import { Tag, ContentWrapper } from '@redocly/theme/components/Tag/Tag';
@@ -39,7 +43,7 @@ export function MenuItem(props: React.PropsWithChildren<MenuItemProps>): JSX.Ele
39
43
  const handleOnClick = () => {
40
44
  telemetry.sendSidebarItemClickedMessage([
41
45
  {
42
- object: 'sidebar_item',
46
+ ...getBaseDataAttributes('sidebarItem', 'sidebar'),
43
47
  label: item.label,
44
48
  type: item.type === 'link' || item.type === 'group' ? item.type : undefined,
45
49
  },
@@ -107,7 +111,13 @@ export function MenuItem(props: React.PropsWithChildren<MenuItemProps>): JSX.Ele
107
111
  {name}
108
112
  </SidebarTag>
109
113
  ))}
110
- <span>{translate(item.labelTranslationKey, item.label)}</span>
114
+ <MenuItemLabelText
115
+ $active={item.active}
116
+ $isSeparator={isSeparator}
117
+ data-text={translate(item.labelTranslationKey, item.label)}
118
+ >
119
+ {translate(item.labelTranslationKey, item.label)}
120
+ </MenuItemLabelText>
111
121
  {item.badges
112
122
  ?.filter(({ position }) => position !== 'before')
113
123
  .map(({ name, color, icon }) => (
@@ -308,7 +318,6 @@ const MenuItemLabelWrapper = styled.li<{
308
318
  css`
309
319
  color: ${$deprecated ? 'var(--menu-content-color-disabled)' : 'var(--menu-item-color-active)'};
310
320
  background-color: var(--menu-item-bg-color-active);
311
- font-weight: var(--menu-item-font-weight-active);
312
321
 
313
322
  ${ChevronDownIcon} path {
314
323
  fill: var(--menu-item-color-active);
@@ -386,6 +395,28 @@ const MenuItemLabel = styled.span`
386
395
  }
387
396
  `;
388
397
 
398
+ const MenuItemLabelText = styled.span<{ $active?: boolean; $isSeparator?: boolean }>`
399
+ display: inline-block;
400
+ max-width: 100%;
401
+ font-weight: var(--menu-item-font-weight);
402
+
403
+ &::before {
404
+ content: attr(data-text);
405
+ display: block;
406
+ height: 0;
407
+ overflow: hidden;
408
+ visibility: hidden;
409
+ font-weight: ${({ $isSeparator }) =>
410
+ $isSeparator ? 'var(--menu-item-font-weight)' : 'var(--menu-item-font-weight-active)'};
411
+ }
412
+
413
+ ${({ $active }) =>
414
+ $active &&
415
+ css`
416
+ font-weight: var(--menu-item-font-weight-active);
417
+ `}
418
+ `;
419
+
389
420
  const SidebarTag = styled(Tag)<{ $bgColor?: string; icon?: React.ReactNode }>`
390
421
  ${({ $bgColor }) => $bgColor && `background-color: ${$bgColor};`} /* for backward compatibility */
391
422
  margin-left: 0;
@@ -4,7 +4,7 @@ import styled from 'styled-components';
4
4
  import type { JSX } from 'react';
5
5
  import type { ResolvedConfigLinks } from '@redocly/config';
6
6
 
7
- import { breakpoints } from '@redocly/theme/core/utils';
7
+ import { breakpoints, getBaseDataAttributes } from '@redocly/theme/core/utils';
8
8
  import { useThemeHooks, useThemeConfig, useMobileMenu } from '@redocly/theme/core/hooks';
9
9
  import { NavbarLogo } from '@redocly/theme/components/Navbar/NavbarLogo';
10
10
  import { NavbarMenu } from '@redocly/theme/components/Navbar/NavbarMenu';
@@ -60,11 +60,15 @@ export function Navbar({ className }: NavbarProps): JSX.Element | null {
60
60
  isOpen
61
61
  ? () => {
62
62
  closeMobileMenu();
63
- telemetry.sendMobileMenuButtonCloseClickedMessage();
63
+ telemetry.sendMobileMenuClosedMessage([
64
+ getBaseDataAttributes('mobileMenuClose', 'button'),
65
+ ]);
64
66
  }
65
67
  : () => {
66
68
  openMobileMenu();
67
- telemetry.sendMobileMenuButtonOpenClickedMessage();
69
+ telemetry.sendMobileMenuOpenedMessage([
70
+ getBaseDataAttributes('mobileMenuOpen', 'button'),
71
+ ]);
68
72
  }
69
73
  }
70
74
  icon={isOpen ? <CloseIcon /> : <MenuIcon />}
@@ -12,6 +12,7 @@ import {
12
12
  getPathnameForLocale,
13
13
  withPathPrefix,
14
14
  removeTrailingSlash,
15
+ getBaseDataAttributes,
15
16
  } from '@redocly/theme/core/utils';
16
17
  import { useThemeHooks } from '@redocly/theme/core/hooks';
17
18
  import { LaunchIcon } from '@redocly/theme/icons/LaunchIcon/LaunchIcon';
@@ -48,7 +49,12 @@ export function NavbarItem({ navItem, className }: NavbarItemProps): JSX.Element
48
49
  $active={isActive}
49
50
  className={className}
50
51
  onClick={() =>
51
- telemetry.sendNavbarMenuItemClickedMessage([{ object: 'menu_item', type: item.type }])
52
+ telemetry.sendNavbarMenuItemClickedMessage([
53
+ {
54
+ ...getBaseDataAttributes('navbarItem', 'navbar'),
55
+ type: item.type,
56
+ },
57
+ ])
52
58
  }
53
59
  external={item.external}
54
60
  target={item.target}
@@ -7,7 +7,7 @@ import type { SearchFacetCount, SearchItemData } from '@redocly/theme/core/types
7
7
  import { SearchInput } from '@redocly/theme/components/Search/SearchInput';
8
8
  import { SearchShortcut } from '@redocly/theme/components/Search/SearchShortcut';
9
9
  import { Button } from '@redocly/theme/components/Button/Button';
10
- import { breakpoints, concatClassNames } from '@redocly/theme/core/utils';
10
+ import { breakpoints, concatClassNames, getBaseDataAttributes } from '@redocly/theme/core/utils';
11
11
  import { Portal } from '@redocly/theme/components/Portal/Portal';
12
12
  import { SearchItem } from '@redocly/theme/components/Search/SearchItem';
13
13
  import { SearchRecent } from '@redocly/theme/components/Search/SearchRecent';
@@ -172,9 +172,8 @@ export function SearchDialog({
172
172
  addSearchHistoryItem(query);
173
173
  telemetry.sendSearchResultClickedMessage([
174
174
  {
175
- object: 'search',
175
+ ...getBaseDataAttributes('searchResultItem', 'search', item.document.url),
176
176
  query: query,
177
- url: item.document.url,
178
177
  totalResults: results.length.toString(),
179
178
  index: index.toString(),
180
179
  searchEngine: mode,
@@ -259,9 +258,7 @@ export function SearchDialog({
259
258
  }
260
259
  telemetry.sendSearchAiOpenedMessage([
261
260
  {
262
- id: 'searchAiButton',
263
- object: 'search',
264
- uri: 'urn:redocly:realm:ui:search:searchAiButton',
261
+ ...getBaseDataAttributes('searchAiButton', 'search'),
265
262
  method: 'ai_search_button',
266
263
  },
267
264
  ]);
@@ -355,9 +352,7 @@ export function SearchDialog({
355
352
  }
356
353
  telemetry.sendSearchAiOpenedMessage([
357
354
  {
358
- id: 'searchAiInput',
359
- object: 'search',
360
- uri: 'urn:redocly:realm:ui:search:searchAiInput',
355
+ ...getBaseDataAttributes('searchAiInput', 'search'),
361
356
  method: 'ai_search_input',
362
357
  },
363
358
  ]);
@@ -370,9 +365,7 @@ export function SearchDialog({
370
365
  }
371
366
  telemetry.sendSearchAiOpenedMessage([
372
367
  {
373
- id: 'searchAiInput',
374
- object: 'search',
375
- uri: 'urn:redocly:realm:ui:search:searchAiInput',
368
+ ...getBaseDataAttributes('searchAiInput', 'search'),
376
369
  method: 'ai_search_input',
377
370
  },
378
371
  ]);
@@ -474,7 +467,7 @@ export function SearchDialog({
474
467
  onSelect={(query, index) => {
475
468
  telemetry.sendSearchRecentClickedMessage([
476
469
  {
477
- object: 'search',
470
+ ...getBaseDataAttributes('searchRecentItem', 'search'),
478
471
  query,
479
472
  index: index.toString(),
480
473
  searchSessionId,
@@ -9,6 +9,7 @@ import { Button } from '@redocly/theme/components/Button/Button';
9
9
  import { useInputKeyCommands, useRecentSearches, useThemeHooks } from '@redocly/theme/core/hooks';
10
10
  import { CloseFilledIcon } from '@redocly/theme/icons/CloseFilledIcon/CloseFilledIcon';
11
11
  import { ChevronLeftIcon } from '@redocly/theme/icons/ChevronLeftIcon/ChevronLeftIcon';
12
+ import { getBaseDataAttributes } from '@redocly/theme/core/utils';
12
13
 
13
14
  export type SearchInputProps = {
14
15
  placeholder?: string;
@@ -51,7 +52,9 @@ export function SearchInput({
51
52
  const handleOnReset = () => {
52
53
  onChange('');
53
54
  addSearchHistoryItem(value);
54
- telemetry.sendSearchInputResetButtonClickedMessage();
55
+ telemetry.sendSearchInputResetClickedMessage([
56
+ getBaseDataAttributes('searchInputReset', 'search'),
57
+ ]);
55
58
  };
56
59
 
57
60
  return (
@@ -3,7 +3,7 @@ import styled from 'styled-components';
3
3
 
4
4
  import type { JSX } from 'react';
5
5
 
6
- import { breakpoints } from '@redocly/theme/core/utils';
6
+ import { breakpoints, getBaseDataAttributes } from '@redocly/theme/core/utils';
7
7
  import { useThemeHooks, useRecentSearches } from '@redocly/theme/core/hooks';
8
8
  import { CloseIcon } from '@redocly/theme/icons/CloseIcon/CloseIcon';
9
9
  import { RecentlyViewedIcon } from '@redocly/theme/icons/RecentlyViewedIcon/RecentlyViewedIcon';
@@ -25,7 +25,9 @@ export function SearchRecent({ onSelect, className }: SearchRecentProps): JSX.El
25
25
  const handleOnRemove = (e: React.MouseEvent<SVGSVGElement, MouseEvent>, item: string) => {
26
26
  e.stopPropagation();
27
27
  removeSearchHistoryItem(item);
28
- telemetry.sendSearchRecentRemoveButtonClickedMessage();
28
+ telemetry.sendSearchRecentRemoveClickedMessage([
29
+ getBaseDataAttributes('searchRecentRemove', 'search'),
30
+ ]);
29
31
  };
30
32
 
31
33
  const handleKeyDown = (e: React.KeyboardEvent<HTMLDivElement>, item: string, index: number) => {
@@ -11,6 +11,7 @@ import {
11
11
  ControlsWrapChangeLayoutButtons,
12
12
  } from '@redocly/theme/components/SidebarActions/styled';
13
13
  import { Tooltip } from '@redocly/theme/components/Tooltip/Tooltip';
14
+ import { getBaseDataAttributes } from '@redocly/theme/core/utils';
14
15
 
15
16
  export { LayoutVariant };
16
17
 
@@ -57,9 +58,13 @@ export const SidebarActions = ({
57
58
  onClick={() => {
58
59
  onChangeCollapseSidebarClick();
59
60
  if (collapsedSidebar) {
60
- telemetry.sendSidebarItemExpandedMessage();
61
+ telemetry.sendSidebarExpandedMessage([
62
+ getBaseDataAttributes('sidebarExpand', 'button'),
63
+ ]);
61
64
  } else {
62
- telemetry.sendSidebarItemCollapsedMessage();
65
+ telemetry.sendSidebarCollapsedMessage([
66
+ getBaseDataAttributes('sidebarCollapse', 'button'),
67
+ ]);
63
68
  }
64
69
  }}
65
70
  size="small"
@@ -76,7 +81,9 @@ export const SidebarActions = ({
76
81
  layout={layout}
77
82
  onClick={() => {
78
83
  onChangeViewClick();
79
- telemetry.sendChangeLayoutButtonClickedMessage();
84
+ telemetry.sendChangeLayoutClickedMessage([
85
+ getBaseDataAttributes('changeLayout', 'button'),
86
+ ]);
80
87
  }}
81
88
  />
82
89
  </ControlsWrapChangeLayoutButtons>
@@ -10,7 +10,12 @@ import {
10
10
  useThemeConfig,
11
11
  useFullHeight,
12
12
  } from '@redocly/theme/core/hooks';
13
- import { breakpoints, getDisplayedHeadings, getLeastDepth } from '@redocly/theme/core/utils';
13
+ import {
14
+ breakpoints,
15
+ getDisplayedHeadings,
16
+ getLeastDepth,
17
+ getBaseDataAttributes,
18
+ } from '@redocly/theme/core/utils';
14
19
 
15
20
  export type TableOfContentProps = {
16
21
  headings?: Array<MdHeading | null> | null | undefined;
@@ -62,7 +67,7 @@ export function TableOfContent(props: TableOfContentProps): JSX.Element | null {
62
67
  data-testid={`toc-${heading.value}`}
63
68
  onClick={(e) => {
64
69
  e.preventDefault();
65
- telemetry.sendTocItemClickedMessage();
70
+ telemetry.sendTocItemClickedMessage([getBaseDataAttributes('tocItem', 'toc')]);
66
71
  handleHeadingClick(heading.id);
67
72
  }}
68
73
  />
@@ -4,6 +4,7 @@ import type { ButtonVariant, ButtonSize } from '@redocly/theme/components/Button
4
4
 
5
5
  import { useThemeHooks } from '@redocly/theme/core/hooks';
6
6
  import { Button } from '@redocly/theme/components/Button/Button';
7
+ import { getBaseDataAttributes } from '@redocly/theme/core/utils';
7
8
 
8
9
  export type LoginButtonProps = {
9
10
  href: string;
@@ -36,7 +37,9 @@ export function LoginButton({
36
37
  data-translation-key={label ? undefined : labelTranslationKey}
37
38
  to={href}
38
39
  languageInsensitive
39
- onClick={() => telemetry.sendLoginButtonClickedMessage()}
40
+ onClick={() =>
41
+ telemetry.sendLoginClickedMessage([getBaseDataAttributes('login', 'button')])
42
+ }
40
43
  data-testid="login-btn"
41
44
  extraClass={className}
42
45
  variant={variant}
@@ -5,6 +5,7 @@ import type { JSX } from 'react';
5
5
  import { useThemeHooks } from '@redocly/theme/core/hooks';
6
6
  import { LogoutIcon } from '@redocly/theme/icons/LogoutIcon/LogoutIcon';
7
7
  import { DropdownMenuItem } from '@redocly/theme/components/Dropdown/DropdownMenuItem';
8
+ import { getBaseDataAttributes } from '@redocly/theme/core/utils';
8
9
 
9
10
  export type LogoutMenuItemProps = {
10
11
  iconOnly?: boolean;
@@ -18,7 +19,7 @@ export function LogoutMenuItem({ iconOnly, className }: LogoutMenuItemProps): JS
18
19
  const { translate } = useTranslate();
19
20
 
20
21
  const handleClick = () => {
21
- telemetry.sendLogoutMenuItemClickedMessage();
22
+ telemetry.sendLogoutClickedMessage([getBaseDataAttributes('logoutItem', 'userMenu')]);
22
23
  handleLogout();
23
24
  };
24
25
 
@@ -5,6 +5,7 @@ import hotkeys from 'hotkeys-js';
5
5
  import { useThemeHooks } from '../use-theme-hooks';
6
6
  import { useThemeConfig } from '../use-theme-config';
7
7
  import { useSearchSession } from '../../contexts';
8
+ import { getBaseDataAttributes } from '../../utils';
8
9
 
9
10
  export function useSearchDialog() {
10
11
  const [isOpen, setIsOpen] = useState(false);
@@ -20,7 +21,12 @@ export function useSearchDialog() {
20
21
  if (hotKeys) {
21
22
  hotkeys(hotKeys, (ev) => {
22
23
  setIsOpen(true);
23
- telemetry.sendSearchOpenedMessage([{ object: 'search', method: 'shortcut' }]);
24
+ telemetry.sendSearchOpenedMessage([
25
+ {
26
+ ...getBaseDataAttributes('searchDialogId', 'search'),
27
+ method: 'shortcut',
28
+ },
29
+ ]);
24
30
  ev.preventDefault();
25
31
  });
26
32
 
@@ -30,7 +36,12 @@ export function useSearchDialog() {
30
36
  }, [hotKeys]);
31
37
 
32
38
  const onOpen = useCallback(function () {
33
- telemetry.sendSearchOpenedMessage([{ object: 'search', method: 'click' }]);
39
+ telemetry.sendSearchOpenedMessage([
40
+ {
41
+ ...getBaseDataAttributes('searchDialogId', 'search'),
42
+ method: 'click',
43
+ },
44
+ ]);
34
45
  setIsOpen(true);
35
46
  // eslint-disable-next-line react-hooks/exhaustive-deps
36
47
  }, []);
@@ -3,6 +3,7 @@ import { useMemo } from 'react';
3
3
  import type { DisplayBanner } from '@redocly/theme/components/Banner/Banner';
4
4
 
5
5
  import { useThemeHooks } from './use-theme-hooks';
6
+ import { getBaseDataAttributes } from '../utils/telemetry/get-base-data-attributes';
6
7
 
7
8
  const noop = (): void => {};
8
9
  const noopLink = (_href: string): void => {};
@@ -28,11 +29,11 @@ export function useBannerTelemetry(
28
29
  };
29
30
  }
30
31
 
31
- const bannerUri = 'urn:redocly:realm:ui:banner:banner-id';
32
32
  const payload = {
33
- id: 'banner-id' as const,
34
- object: 'banner' as const,
35
- uri: bannerUri,
33
+ ...getBaseDataAttributes<'bannerId', 'banner', 'urn:redocly:realm:ui:banner:bannerId'>(
34
+ 'bannerId',
35
+ 'banner',
36
+ ),
36
37
  trackingId: displayBanner.trackingId,
37
38
  hash: displayBanner.hash,
38
39
  color: displayBanner.color,
@@ -6,6 +6,7 @@ import { useThemeConfig } from './use-theme-config';
6
6
  import { useThemeHooks } from './use-theme-hooks';
7
7
  import { createStore, useStore } from './use-store';
8
8
  import { DEFAULT_COLOR_MODES } from '../constants';
9
+ import { generateBeforeAfterContext } from '../utils';
9
10
 
10
11
  const COLOR_MODE_KEY = 'colorSchema';
11
12
  const colorModeStore = createStore<string>({
@@ -51,7 +52,12 @@ export const useColorSwitcher = () => {
51
52
  root.classList.remove('notransition');
52
53
  });
53
54
  telemetry.sendColorModeSwitchedMessage([
54
- { object: 'color_mode', from: activeColorMode, to: newMode },
55
+ ...generateBeforeAfterContext<'sendColorModeSwitchedMessage'>(
56
+ 'colorMode',
57
+ 'button',
58
+ { mode: activeColorMode },
59
+ { mode: newMode },
60
+ ),
55
61
  ]);
56
62
  setActiveColorMode(newMode);
57
63
  };