@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
@@ -47,6 +47,7 @@ const Tooltip_1 = require("../../components/Tooltip/Tooltip");
47
47
  const GenericIcon_1 = require("../../icons/GenericIcon/GenericIcon");
48
48
  const constants_1 = require("../../core/constants");
49
49
  const BreadcrumbIcon_1 = require("../../components/Breadcrumbs/BreadcrumbIcon");
50
+ const utils_1 = require("../../core/utils");
50
51
  function BreadcrumbDropdown({ children, label, items, onItemClick, className, withChevron, }) {
51
52
  const { useTelemetry, useTranslate } = (0, hooks_1.useThemeHooks)();
52
53
  const telemetry = useTelemetry();
@@ -70,12 +71,7 @@ function BreadcrumbDropdown({ children, label, items, onItemClick, className, wi
70
71
  return (react_1.default.createElement(StyledDropdownMenuItem, { key: index, onAction: () => {
71
72
  onItemClick === null || onItemClick === void 0 ? void 0 : onItemClick(item, index);
72
73
  telemetry.sendBreadcrumbClickedMessage([
73
- {
74
- object: 'breadcrumb',
75
- link: item.link,
76
- position: index + 1,
77
- totalBreadcrumbs: items.length,
78
- },
74
+ Object.assign(Object.assign({}, (0, utils_1.getBaseDataAttributes)('breadcrumbId', 'breadcrumb')), { link: item.link, position: index + 1, totalBreadcrumbs: items.length }),
79
75
  ]);
80
76
  }, $hasLink: hasLink, to: item.link, dataAttributes: !hasLink ? { 'aria-disabled': 'true' } : {} },
81
77
  react_1.default.createElement(DropdownContent, { $isActive: isActive },
@@ -46,23 +46,13 @@ function Breadcrumbs(props) {
46
46
  ];
47
47
  const translatedLabel = translate(breadcrumb.labelTranslationKey, breadcrumb.label);
48
48
  return (react_1.default.createElement(BreadcrumbDropdown_1.BreadcrumbDropdown, { label: translatedLabel, items: siblingsWithActive, withChevron: true, onItemClick: (item, itemIdx) => telemetry.sendBreadcrumbClickedMessage([
49
- {
50
- object: 'breadcrumb',
51
- link: item.link,
52
- position: itemIdx + 1,
53
- totalBreadcrumbs: siblingsWithActive.length,
54
- },
49
+ Object.assign(Object.assign({}, (0, utils_1.getBaseDataAttributes)('breadcrumbId', 'breadcrumb')), { link: item.link, position: itemIdx + 1, totalBreadcrumbs: siblingsWithActive.length }),
55
50
  ]) },
56
51
  react_1.default.createElement(BreadcrumbIcon_1.BreadcrumbIcon, { icon: breadcrumb.icon }),
57
52
  (0, utils_1.trimText)(translatedLabel, constants_1.BREADCRUMB_MAX_LENGTH)));
58
53
  }
59
54
  return (react_1.default.createElement(Breadcrumb_1.Breadcrumb, { link: breadcrumb.link, label: translate(breadcrumb.labelTranslationKey, breadcrumb.label), isActive: isActive, icon: breadcrumb.icon, onClick: () => telemetry.sendBreadcrumbClickedMessage([
60
- {
61
- object: 'breadcrumb',
62
- link: breadcrumb.link,
63
- position: idx + 1,
64
- totalBreadcrumbs: breadcrumbs.length,
65
- },
55
+ Object.assign(Object.assign({}, (0, utils_1.getBaseDataAttributes)('breadcrumbId', 'breadcrumb')), { link: breadcrumb.link, position: idx + 1, totalBreadcrumbs: breadcrumbs.length }),
66
56
  ]) }));
67
57
  };
68
58
  return (react_1.default.createElement(BreadcrumbsWrapper, { "data-component-name": "Breadcrumbs/Breadcrumbs", className: props.className }, items.map(({ breadcrumb, idx, isLast, inDropdown }) => {
@@ -74,12 +64,7 @@ function Breadcrumbs(props) {
74
64
  renderBreadcrumb(breadcrumb, idx, isLast),
75
65
  react_1.default.createElement(BreadcrumbSeparator, null, "/"),
76
66
  react_1.default.createElement(BreadcrumbDropdown_1.BreadcrumbDropdown, { label: "...", items: collapsedBreadcrumbs, onItemClick: (item, itemIdx) => telemetry.sendBreadcrumbClickedMessage([
77
- {
78
- object: 'breadcrumb',
79
- link: item.link,
80
- position: itemIdx + 1,
81
- totalBreadcrumbs: breadcrumbs.length,
82
- },
67
+ Object.assign(Object.assign({}, (0, utils_1.getBaseDataAttributes)('breadcrumbId', 'breadcrumb')), { link: item.link, position: itemIdx + 1, totalBreadcrumbs: breadcrumbs.length }),
83
68
  ]) }, "..."),
84
69
  react_1.default.createElement(BreadcrumbSeparator, null, "/")));
85
70
  }
@@ -47,6 +47,7 @@ const ChatIcon_1 = require("../../icons/ChatIcon/ChatIcon");
47
47
  const AiStarsGradientIcon_1 = require("../../icons/AiStarsGradientIcon/AiStarsGradientIcon");
48
48
  const RedoclyIcon_1 = require("../../icons/RedoclyIcon/RedoclyIcon");
49
49
  const contexts_1 = require("../../core/contexts");
50
+ const utils_1 = require("../../core/utils");
50
51
  const defaultConfig = {
51
52
  hide: false,
52
53
  inputType: 'button',
@@ -88,12 +89,7 @@ function AIAssistantButton() {
88
89
  const handleOpen = () => {
89
90
  setIsOpen(true);
90
91
  telemetry.sendSearchAiOpenedMessage([
91
- {
92
- id: 'aiAssistantTriggerButton',
93
- object: 'search',
94
- uri: 'urn:redocly:realm:ui:search:aiAssistantTriggerButton',
95
- method: 'ai_trigger_button',
96
- },
92
+ Object.assign(Object.assign({}, (0, utils_1.getBaseDataAttributes)('aiAssistantTriggerButton', 'search')), { method: 'ai_trigger_button' }),
97
93
  ]);
98
94
  };
99
95
  const handleClose = () => {
@@ -9,12 +9,13 @@ const styled_components_1 = __importDefault(require("styled-components"));
9
9
  const hooks_1 = require("../../core/hooks");
10
10
  const EditIcon_1 = require("../../icons/EditIcon/EditIcon");
11
11
  const Button_1 = require("../../components/Button/Button");
12
+ const utils_1 = require("../../core/utils");
12
13
  function EditPageButton({ to }) {
13
14
  const { useTranslate, useTelemetry } = (0, hooks_1.useThemeHooks)();
14
15
  const { translate } = useTranslate();
15
16
  const telemetry = useTelemetry();
16
17
  return (react_1.default.createElement(EditPageButtonWrapper, { "data-component-name": "Buttons/EditPageButton" },
17
- react_1.default.createElement(Button_1.Button, { to: to, external: true, variant: "ghost", size: "small", icon: react_1.default.createElement(EditIcon_1.EditIcon, null), onClick: () => telemetry.sendEditPageLinkClickedMessage(), "data-translation-key": "markdown.editPage.text" }, translate('markdown.editPage.text', 'Edit'))));
18
+ react_1.default.createElement(Button_1.Button, { to: to, external: true, variant: "ghost", size: "small", icon: react_1.default.createElement(EditIcon_1.EditIcon, null), onClick: () => telemetry.sendPageEditClickedMessage([(0, utils_1.getBaseDataAttributes)('pageEdit', 'button')]), "data-translation-key": "markdown.editPage.text" }, translate('markdown.editPage.text', 'Edit'))));
18
19
  }
19
20
  const EditPageButtonWrapper = styled_components_1.default.div `
20
21
  margin-left: auto;
@@ -23,7 +23,7 @@ function CatalogCard({ entity, catalogConfig }) {
23
23
  const pathPrefix = (0, utils_1.getPathPrefix)();
24
24
  return (react_1.default.createElement(CatalogCardWrapper, { "data-component-name": "Catalog/CatalogCardView/CatalogCard", onClick: () => {
25
25
  window.location.assign(`${pathPrefix}/catalogs/${catalogConfig.slug}/entities/${entity.key}`);
26
- telemetry.sendCatalogItemClickedMessage();
26
+ telemetry.sendCatalogItemClickedMessage([(0, utils_1.getBaseDataAttributes)('catalogItem', 'card')]);
27
27
  } },
28
28
  react_1.default.createElement(CardContent, null,
29
29
  react_1.default.createElement(CardHeader, null,
@@ -61,7 +61,9 @@ function CatalogClassicActions(props) {
61
61
  return (React.createElement(CatalogActionsWrapper, { "data-component-name": "CatalogClassic/CatalogClassicActions" },
62
62
  React.createElement(Button_1.Button, { variant: "ghost", size: "small", icon: React.createElement(FilterIcon_1.FilterIcon, null), iconPosition: "left", onClick: () => {
63
63
  onOpenFilter();
64
- telemetry.sendCatalogActionsButtonClickedMessage();
64
+ telemetry.sendCatalogActionsClickedMessage([
65
+ (0, utils_1.getBaseDataAttributes)('catalogActions', 'button'),
66
+ ]);
65
67
  }, "data-translation-key": "catalog.filters.title" }, translate('catalog.filters.title', 'Filters')),
66
68
  activeFilters > 0 ? React.createElement(CounterTag_1.CounterTag, { borderless: true }, activeFilters) : null));
67
69
  }
@@ -64,7 +64,7 @@ function CatalogClassicCard({ item }) {
64
64
  const { useTelemetry } = (0, hooks_1.useThemeHooks)();
65
65
  const telemetry = useTelemetry();
66
66
  return (React.createElement(Link_1.Link, { key: item.docsLink || item.link, to: item.docsLink || item.link },
67
- React.createElement(StyledCard, { "data-component-name": "CatalogClassic/CatalogClassicCard", onClick: () => telemetry.sendCatalogItemClickedMessage() },
67
+ React.createElement(StyledCard, { "data-component-name": "CatalogClassic/CatalogClassicCard", onClick: () => telemetry.sendCatalogItemClickedMessage([(0, utils_1.getBaseDataAttributes)('catalogItem', 'card')]) },
68
68
  React.createElement(CardContent, null,
69
69
  React.createElement(CardTitleWrapper, null,
70
70
  React.createElement(CardTitle, null,
@@ -10,6 +10,7 @@ const hooks_1 = require("../../core/hooks");
10
10
  const utils_1 = require("../../core/utils");
11
11
  const Tag_1 = require("../../components/Tag/Tag");
12
12
  const Link_1 = require("../../components/Link/Link");
13
+ const utils_2 = require("../../core/utils");
13
14
  function CatalogClassicInfoBlock(props) {
14
15
  var _a, _b, _c, _d;
15
16
  const scorecardBadge = ((_a = props.metadata) === null || _a === void 0 ? void 0 : _a.scorecardLevel) ? (react_1.default.createElement(ScorecardBadge, { level: props.metadata.scorecardLevel, slug: (_b = props.metadata) === null || _b === void 0 ? void 0 : _b.scoreCardSlug, colorVariable: (0, utils_1.getScorecardColorVariable)(((_c = props.metadata) === null || _c === void 0 ? void 0 : _c.scorecardLevelIdx) || 0, Object.keys(((_d = props.metadata) === null || _d === void 0 ? void 0 : _d.scorecardLevels) || {}).length) })) : null;
@@ -21,7 +22,7 @@ function ScorecardBadge(props) {
21
22
  const { useTelemetry } = (0, hooks_1.useThemeHooks)();
22
23
  const telemetry = useTelemetry();
23
24
  return (react_1.default.createElement(Link_1.Link, { to: slug },
24
- react_1.default.createElement(Tag_1.Tag, { onClick: () => telemetry.sendScorecardLinkClickedMessage([{ object: 'link', action: 'click' }]), withStatusDot: true, statusDotColor: `var(${colorVariable})` }, level)));
25
+ react_1.default.createElement(Tag_1.Tag, { onClick: () => telemetry.sendScorecardLinkClickedMessage([(0, utils_2.getBaseDataAttributes)('scorecardLink', 'tag')]), withStatusDot: true, statusDotColor: `var(${colorVariable})` }, level)));
25
26
  }
26
27
  const CatalogInfoBlockWrapper = styled_components_1.default.div `
27
28
  position: relative;
@@ -19,6 +19,7 @@ const react_router_dom_1 = require("react-router-dom");
19
19
  const hooks_1 = require("../../core/hooks");
20
20
  const Comment_1 = require("../../components/Feedback/Comment");
21
21
  const Portal_1 = require("../../components/Portal/Portal");
22
+ const utils_1 = require("../../core/utils");
22
23
  function ReportDialog({ location, settings, onSubmit, onCancel, submitFeedback, lang, }) {
23
24
  const { label } = settings;
24
25
  const { useTelemetry } = (0, hooks_1.useThemeHooks)();
@@ -34,7 +35,9 @@ function ReportDialog({ location, settings, onSubmit, onCancel, submitFeedback,
34
35
  path: pathname,
35
36
  lang,
36
37
  });
37
- telemetry.sendCodeSnippetReportedMessage();
38
+ telemetry.sendCodeSnippetReportedMessage([
39
+ (0, utils_1.getBaseDataAttributes)('codeSnippet', 'markdocTag'),
40
+ ]);
38
41
  onSubmit();
39
42
  }), isDialog: true, onCancel: onCancel }))));
40
43
  }
@@ -24,7 +24,9 @@ function FilterCheckboxes({ filter, filterValuesCasing, showCounter = true, }) {
24
24
  const id = 'filter--' + filter.property + '--' + value;
25
25
  return (react_1.default.createElement(FilterCheckboxOption, { key: id, role: "link", onClick: () => {
26
26
  filter.toggleOption(value);
27
- telemetry.sendFilterCheckboxToggledMessage([{ object: 'checkbox', id }]);
27
+ telemetry.sendFilterCheckboxToggledMessage([
28
+ (0, utils_1.getBaseDataAttributes)('filterId', 'checkbox'),
29
+ ]);
28
30
  } },
29
31
  react_1.default.createElement(CheckboxIcon_1.CheckboxIcon, { checked: filter.selectedOptions instanceof Set
30
32
  ? filter.selectedOptions.has(value) ||
@@ -20,7 +20,7 @@ function FooterItem({ item, iconsOnly, className }) {
20
20
  }
21
21
  const hasIcon = Boolean(item.icon || item.srcSet);
22
22
  const iconWithoutLabel = Boolean(item.label === item.link && hasIcon);
23
- return (react_1.default.createElement(FooterItemWrapper, { className: className, "data-component-name": "Footer/FooterItem", $iconsOnly: iconsOnly, $item: item }, item.type === 'separator' ? (react_1.default.createElement(FooterSeparator, { "data-translation-key": item.labelTranslationKey }, translate(item.labelTranslationKey, item.label))) : (react_1.default.createElement(FooterLink, { to: item.link, external: item.external, target: item.target, "data-testid": item.label, onClick: () => telemetry.sendFooterItemClickedMessage(), "data-translation-key": item.labelTranslationKey },
23
+ return (react_1.default.createElement(FooterItemWrapper, { className: className, "data-component-name": "Footer/FooterItem", $iconsOnly: iconsOnly, $item: item }, item.type === 'separator' ? (react_1.default.createElement(FooterSeparator, { "data-translation-key": item.labelTranslationKey }, translate(item.labelTranslationKey, item.label))) : (react_1.default.createElement(FooterLink, { to: item.link, external: item.external, target: item.target, "data-testid": item.label, onClick: () => telemetry.sendFooterItemClickedMessage([(0, utils_1.getBaseDataAttributes)('footerItem', 'footer')]), "data-translation-key": item.labelTranslationKey },
24
24
  hasIcon ? (react_1.default.createElement(FooterLinkIcon, { $iconsOnly: iconsOnly },
25
25
  react_1.default.createElement(GenericIcon_1.GenericIcon, { icon: item.icon, srcSet: item.srcSet }))) : null,
26
26
  !iconWithoutLabel ? translate(item.labelTranslationKey, item.label) : null,
@@ -27,7 +27,9 @@ function LanguagePicker(props) {
27
27
  languageInsensitive: true,
28
28
  onAction: () => {
29
29
  props.onChangeLanguage(locale.code);
30
- telemetry.sendLanguagePickerLocaleChangedMessage([{ object: 'locale', locale: locale.code }]);
30
+ telemetry.sendLanguagePickerChangedMessage([
31
+ ...(0, utils_1.generateBeforeAfterContext)('languagePicker', 'dropdown', { locale: currentLocale.code }, { locale: locale.code }),
32
+ ]);
31
33
  },
32
34
  active: locale.code === currentLocale.code,
33
35
  suffix: locale.code === currentLocale.code && react_1.default.createElement(CheckmarkIcon_1.CheckmarkIcon, null),
@@ -20,6 +20,7 @@ const styled_components_1 = __importDefault(require("styled-components"));
20
20
  const hooks_1 = require("../../core/hooks");
21
21
  const Link_1 = require("../../components/Link/Link");
22
22
  const Image_1 = require("../../components/Image/Image");
23
+ const utils_1 = require("../../core/utils");
23
24
  function Logo(_a) {
24
25
  var { config, className } = _a, otherProps = __rest(_a, ["config", "className"]);
25
26
  const { useTelemetry } = (0, hooks_1.useThemeHooks)();
@@ -28,7 +29,7 @@ function Logo(_a) {
28
29
  return null;
29
30
  }
30
31
  const image = (react_1.default.createElement(Image_1.Image, { className: className, src: config.image, srcSet: config.srcSet, alt: config.altText }));
31
- return (react_1.default.createElement(LogoWrapper, Object.assign({ "data-component-name": "Logo/Logo", className: className }, otherProps), config.link ? (react_1.default.createElement(Link_1.Link, { to: config.link, onClick: () => telemetry.sendLogoClickedMessage() }, image)) : (image)));
32
+ return (react_1.default.createElement(LogoWrapper, Object.assign({ "data-component-name": "Logo/Logo", className: className }, otherProps), config.link ? (react_1.default.createElement(Link_1.Link, { to: config.link, onClick: () => telemetry.sendLogoClickedMessage([(0, utils_1.getBaseDataAttributes)('logo', 'navbar')]) }, image)) : (image)));
32
33
  }
33
34
  const LogoWrapper = styled_components_1.default.div `
34
35
  max-width: var(--logo-max-width);
@@ -71,6 +71,7 @@ const MenuContainerComponent = styled_components_1.default.div.attrs({
71
71
  animation-timing-function: ease;
72
72
  position: relative;
73
73
  overflow-y: auto;
74
+ scrollbar-gutter: stable;
74
75
  flex-grow: ${({ $growContent }) => ($growContent ? 1 : 0)};
75
76
  padding-top: var(--menu-container-padding-top);
76
77
  display: ${({ $hidden }) => ($hidden ? 'none' : 'block')};
@@ -66,11 +66,7 @@ function MenuItem(props) {
66
66
  const hasHttpTag = !!item.httpVerb || type === constants_1.MenuItemType.Operation;
67
67
  const handleOnClick = () => {
68
68
  telemetry.sendSidebarItemClickedMessage([
69
- {
70
- object: 'sidebar_item',
71
- label: item.label,
72
- type: item.type === 'link' || item.type === 'group' ? item.type : undefined,
73
- },
69
+ Object.assign(Object.assign({}, (0, utils_1.getBaseDataAttributes)('sidebarItem', 'sidebar')), { label: item.label, type: item.type === 'link' || item.type === 'group' ? item.type : undefined }),
74
70
  ]);
75
71
  onClick === null || onClick === void 0 ? void 0 : onClick();
76
72
  if (isNested) {
@@ -95,7 +91,7 @@ function MenuItem(props) {
95
91
  react_1.default.createElement(MenuItemLabel, null,
96
92
  (_a = item.badges) === null || _a === void 0 ? void 0 :
97
93
  _a.filter(({ position }) => position === 'before').map(({ name, color, icon }) => (react_1.default.createElement(SidebarTag, { color: isDirectColorValue(color) ? undefined : color, $bgColor: isDirectColorValue(color) ? color : undefined, key: name, icon: icon && react_1.default.createElement(BadgeIcon, { icon: icon }) }, name))),
98
- react_1.default.createElement("span", null, translate(item.labelTranslationKey, item.label)),
94
+ react_1.default.createElement(MenuItemLabelText, { $active: item.active, $isSeparator: isSeparator, "data-text": translate(item.labelTranslationKey, item.label) }, translate(item.labelTranslationKey, item.label)),
99
95
  (_b = item.badges) === null || _b === void 0 ? void 0 :
100
96
  _b.filter(({ position }) => position !== 'before').map(({ name, color, icon }) => (react_1.default.createElement(SidebarTag, { color: isDirectColorValue(color) ? undefined : color, $bgColor: isDirectColorValue(color) ? color : undefined, key: name, icon: icon && react_1.default.createElement(BadgeIcon, { icon: icon }) }, name))),
101
97
  item.external ? react_1.default.createElement(LaunchIcon_1.LaunchIcon, { size: "var(--menu-item-external-icon-size)" }) : null),
@@ -214,7 +210,6 @@ const MenuItemLabelWrapper = styled_components_1.default.li `
214
210
  (0, styled_components_1.css) `
215
211
  color: ${$deprecated ? 'var(--menu-content-color-disabled)' : 'var(--menu-item-color-active)'};
216
212
  background-color: var(--menu-item-bg-color-active);
217
- font-weight: var(--menu-item-font-weight-active);
218
213
 
219
214
  ${ChevronDownIcon_1.ChevronDownIcon} path {
220
215
  fill: var(--menu-item-color-active);
@@ -281,6 +276,25 @@ const MenuItemLabel = styled_components_1.default.span `
281
276
  margin-right: var(--spacing-xxs);
282
277
  }
283
278
  `;
279
+ const MenuItemLabelText = styled_components_1.default.span `
280
+ display: inline-block;
281
+ max-width: 100%;
282
+ font-weight: var(--menu-item-font-weight);
283
+
284
+ &::before {
285
+ content: attr(data-text);
286
+ display: block;
287
+ height: 0;
288
+ overflow: hidden;
289
+ visibility: hidden;
290
+ font-weight: ${({ $isSeparator }) => $isSeparator ? 'var(--menu-item-font-weight)' : 'var(--menu-item-font-weight-active)'};
291
+ }
292
+
293
+ ${({ $active }) => $active &&
294
+ (0, styled_components_1.css) `
295
+ font-weight: var(--menu-item-font-weight-active);
296
+ `}
297
+ `;
284
298
  const SidebarTag = (0, styled_components_1.default)(Tag_1.Tag) `
285
299
  ${({ $bgColor }) => $bgColor && `background-color: ${$bgColor};`} /* for backward compatibility */
286
300
  margin-left: 0;
@@ -47,11 +47,15 @@ function Navbar({ className }) {
47
47
  react_1.default.createElement(MobileMenuButton, { variant: "text", "data-testid": "mobile-menu-button", onClick: isOpen
48
48
  ? () => {
49
49
  closeMobileMenu();
50
- telemetry.sendMobileMenuButtonCloseClickedMessage();
50
+ telemetry.sendMobileMenuClosedMessage([
51
+ (0, utils_1.getBaseDataAttributes)('mobileMenuClose', 'button'),
52
+ ]);
51
53
  }
52
54
  : () => {
53
55
  openMobileMenu();
54
- telemetry.sendMobileMenuButtonOpenClickedMessage();
56
+ telemetry.sendMobileMenuOpenedMessage([
57
+ (0, utils_1.getBaseDataAttributes)('mobileMenuOpen', 'button'),
58
+ ]);
55
59
  }, icon: isOpen ? react_1.default.createElement(CloseIcon_1.CloseIcon, null) : react_1.default.createElement(MenuIcon_1.MenuIcon, null), "aria-label": isOpen ? 'Close menu button' : 'Open menu button' }),
56
60
  hideUserMenu ? null : react_1.default.createElement(UserMenu_1.UserMenu, null))));
57
61
  }
@@ -27,7 +27,9 @@ function NavbarItem({ navItem, className }) {
27
27
  const normalizedPath = (item.link ? (0, utils_1.removeTrailingSlash)(item.link) : item.link) || '';
28
28
  const pathWithPathPrefix = (0, utils_1.withPathPrefix)((0, utils_1.getPathnameForLocale)(normalizedPath, defaultLocale, currentLocale, locales));
29
29
  const isActive = (0, utils_1.removeTrailingSlash)(pathname) === (0, utils_1.removeTrailingSlash)(pathWithPathPrefix);
30
- const itemContent = (react_1.default.createElement(NavbarMenuItem, { as: item.link ? Link_1.Link : undefined, $active: isActive, className: className, onClick: () => telemetry.sendNavbarMenuItemClickedMessage([{ object: 'menu_item', type: item.type }]), external: item.external, target: item.target, to: item.link },
30
+ const itemContent = (react_1.default.createElement(NavbarMenuItem, { as: item.link ? Link_1.Link : undefined, $active: isActive, className: className, onClick: () => telemetry.sendNavbarMenuItemClickedMessage([
31
+ Object.assign(Object.assign({}, (0, utils_1.getBaseDataAttributes)('navbarItem', 'navbar')), { type: item.type }),
32
+ ]), external: item.external, target: item.target, to: item.link },
31
33
  react_1.default.createElement(NavbarIcon, { icon: item.icon, srcSet: item.srcSet }),
32
34
  react_1.default.createElement(NavbarLabel, { "data-translation-key": item.labelTranslationKey }, translate(item.labelTranslationKey, item.label)),
33
35
  item.external ? react_1.default.createElement(ExternalLinkIcon, { size: "10px" }) : null));
@@ -141,15 +141,7 @@ function SearchDialog({ onClose, className, initialMode = 'search', }) {
141
141
  return (react_1.default.createElement(SearchItem_1.SearchItem, { key: `${index}-${item.document.id}`, item: item, product: itemProduct, innerRef: innerRef, onClick: () => {
142
142
  addSearchHistoryItem(query);
143
143
  telemetry.sendSearchResultClickedMessage([
144
- {
145
- object: 'search',
146
- query: query,
147
- url: item.document.url,
148
- totalResults: results.length.toString(),
149
- index: index.toString(),
150
- searchEngine: mode,
151
- searchSessionId,
152
- },
144
+ Object.assign(Object.assign({}, (0, utils_1.getBaseDataAttributes)('searchResultItem', 'search', item.document.url)), { query: query, totalResults: results.length.toString(), index: index.toString(), searchEngine: mode, searchSessionId }),
153
145
  ]);
154
146
  onClose();
155
147
  } }));
@@ -200,12 +192,7 @@ function SearchDialog({ onClose, className, initialMode = 'search', }) {
200
192
  aiSearch.askQuestion(query);
201
193
  }
202
194
  telemetry.sendSearchAiOpenedMessage([
203
- {
204
- id: 'searchAiButton',
205
- object: 'search',
206
- uri: 'urn:redocly:realm:ui:search:searchAiButton',
207
- method: 'ai_search_button',
208
- },
195
+ Object.assign(Object.assign({}, (0, utils_1.getBaseDataAttributes)('searchAiButton', 'search')), { method: 'ai_search_button' }),
209
196
  ]);
210
197
  } }, translate('search.ai.button', 'Search with AI'))) : null,
211
198
  showSearchFilterButton && (react_1.default.createElement(SearchFilterToggleButton, { icon: react_1.default.createElement(SettingsIcon_1.SettingsIcon, null), onClick: onFilterToggle })))))) : (react_1.default.createElement(AiDialogHeaderWrapper, null,
@@ -235,12 +222,7 @@ function SearchDialog({ onClose, className, initialMode = 'search', }) {
235
222
  aiSearch.askQuestion(query);
236
223
  }
237
224
  telemetry.sendSearchAiOpenedMessage([
238
- {
239
- id: 'searchAiInput',
240
- object: 'search',
241
- uri: 'urn:redocly:realm:ui:search:searchAiInput',
242
- method: 'ai_search_input',
243
- },
225
+ Object.assign(Object.assign({}, (0, utils_1.getBaseDataAttributes)('searchAiInput', 'search')), { method: 'ai_search_input' }),
244
226
  ]);
245
227
  }, onKeyDown: (e) => {
246
228
  if (e.key === 'Enter') {
@@ -249,12 +231,7 @@ function SearchDialog({ onClose, className, initialMode = 'search', }) {
249
231
  aiSearch.askQuestion(query);
250
232
  }
251
233
  telemetry.sendSearchAiOpenedMessage([
252
- {
253
- id: 'searchAiInput',
254
- object: 'search',
255
- uri: 'urn:redocly:realm:ui:search:searchAiInput',
256
- method: 'ai_search_input',
257
- },
234
+ Object.assign(Object.assign({}, (0, utils_1.getBaseDataAttributes)('searchAiInput', 'search')), { method: 'ai_search_input' }),
258
235
  ]);
259
236
  }
260
237
  }, ref: aiQueryRef, tabIndex: 0, role: "option", "aria-selected": "true" },
@@ -293,12 +270,7 @@ function SearchDialog({ onClose, className, initialMode = 'search', }) {
293
270
  react_1.default.createElement("b", null, translate('search.noResults.title', 'No results'))))) : (react_1.default.createElement(react_1.default.Fragment, null,
294
271
  react_1.default.createElement(SearchRecent_1.SearchRecent, { onSelect: (query, index) => {
295
272
  telemetry.sendSearchRecentClickedMessage([
296
- {
297
- object: 'search',
298
- query,
299
- index: index.toString(),
300
- searchSessionId,
301
- },
273
+ Object.assign(Object.assign({}, (0, utils_1.getBaseDataAttributes)('searchRecentItem', 'search')), { query, index: index.toString(), searchSessionId }),
302
274
  ]);
303
275
  setQuery(query);
304
276
  focusSearchInput();
@@ -12,6 +12,7 @@ const Button_1 = require("../../components/Button/Button");
12
12
  const hooks_1 = require("../../core/hooks");
13
13
  const CloseFilledIcon_1 = require("../../icons/CloseFilledIcon/CloseFilledIcon");
14
14
  const ChevronLeftIcon_1 = require("../../icons/ChevronLeftIcon/ChevronLeftIcon");
15
+ const utils_1 = require("../../core/utils");
15
16
  function SearchInput({ placeholder, value, onChange, isLoading, showReturnButton, inputRef, onReturn, onSubmit, className, }) {
16
17
  const { useTelemetry } = (0, hooks_1.useThemeHooks)();
17
18
  const { addSearchHistoryItem } = (0, hooks_1.useRecentSearches)();
@@ -27,7 +28,9 @@ function SearchInput({ placeholder, value, onChange, isLoading, showReturnButton
27
28
  const handleOnReset = () => {
28
29
  onChange('');
29
30
  addSearchHistoryItem(value);
30
- telemetry.sendSearchInputResetButtonClickedMessage();
31
+ telemetry.sendSearchInputResetClickedMessage([
32
+ (0, utils_1.getBaseDataAttributes)('searchInputReset', 'search'),
33
+ ]);
31
34
  };
32
35
  return (react_1.default.createElement(SearchInputWrapper, { "data-component-name": "Search/SearchInput", className: className },
33
36
  showReturnButton ? (react_1.default.createElement(Button_1.Button, { icon: react_1.default.createElement(ChevronLeftIcon_1.ChevronLeftIcon, null), onClick: onReturn })) : value && isLoading ? (react_1.default.createElement(Spinner_1.Spinner, { size: "24px", color: "--search-input-icon-color" })) : (react_1.default.createElement(SearchIcon_1.SearchIcon, { size: "24px", color: "--search-input-icon-color" })),
@@ -54,7 +54,9 @@ function SearchRecent({ onSelect, className }) {
54
54
  const handleOnRemove = (e, item) => {
55
55
  e.stopPropagation();
56
56
  removeSearchHistoryItem(item);
57
- telemetry.sendSearchRecentRemoveButtonClickedMessage();
57
+ telemetry.sendSearchRecentRemoveClickedMessage([
58
+ (0, utils_1.getBaseDataAttributes)('searchRecentRemove', 'search'),
59
+ ]);
58
60
  };
59
61
  const handleKeyDown = (e, item, index) => {
60
62
  if (e.key === 'Enter') {
@@ -14,6 +14,7 @@ const SidePanelCloseIcon_1 = require("../../icons/SidePanelCloseIcon/SidePanelCl
14
14
  const SidePanelOpenIcon_1 = require("../../icons/SidePanelOpenIcon/SidePanelOpenIcon");
15
15
  const styled_1 = require("../../components/SidebarActions/styled");
16
16
  const Tooltip_1 = require("../../components/Tooltip/Tooltip");
17
+ const utils_1 = require("../../core/utils");
17
18
  const SidebarActions = ({ layout, hideCollapseSidebarButton = false, collapsedSidebar, isApiDocs, onChangeViewClick, onChangeCollapseSidebarClick, requestAccessButton, className, }) => {
18
19
  const { useTelemetry, useTranslate } = (0, hooks_1.useThemeHooks)();
19
20
  const { translate } = useTranslate();
@@ -24,16 +25,22 @@ const SidebarActions = ({ layout, hideCollapseSidebarButton = false, collapsedSi
24
25
  : translate('sidebar.actions.hide', 'Hide sidebar') }, react_1.default.createElement(Button_1.Button, { onClick: () => {
25
26
  onChangeCollapseSidebarClick();
26
27
  if (collapsedSidebar) {
27
- telemetry.sendSidebarItemExpandedMessage();
28
+ telemetry.sendSidebarExpandedMessage([
29
+ (0, utils_1.getBaseDataAttributes)('sidebarExpand', 'button'),
30
+ ]);
28
31
  }
29
32
  else {
30
- telemetry.sendSidebarItemCollapsedMessage();
33
+ telemetry.sendSidebarCollapsedMessage([
34
+ (0, utils_1.getBaseDataAttributes)('sidebarCollapse', 'button'),
35
+ ]);
31
36
  }
32
37
  }, size: "small", variant: "outlined", "aria-label": collapsedSidebar ? 'Show sidebar' : 'Hide sidebar', icon: collapsedSidebar ? react_1.default.createElement(SidePanelOpenIcon_1.SidePanelOpenIcon, null) : react_1.default.createElement(SidePanelCloseIcon_1.SidePanelCloseIcon, null) }))),
33
38
  isApiDocs && (react_1.default.createElement(styled_1.ControlsWrapChangeLayoutButtons, { isCollapsed: collapsedSidebar },
34
39
  react_1.default.createElement(ChangeViewButton_1.ChangeViewButton, { collapsedSidebar: collapsedSidebar, layout: layout, onClick: () => {
35
40
  onChangeViewClick();
36
- telemetry.sendChangeLayoutButtonClickedMessage();
41
+ telemetry.sendChangeLayoutClickedMessage([
42
+ (0, utils_1.getBaseDataAttributes)('changeLayout', 'button'),
43
+ ]);
37
44
  } }))),
38
45
  !collapsedSidebar && requestAccessButton));
39
46
  };
@@ -65,7 +65,7 @@ function TableOfContent(props) {
65
65
  const href = '#' + heading.id;
66
66
  return (React.createElement(TableOfContentMenuItem, { key: href + idx, href: href, $depth: heading.depth - leastDepth + 1 || 0, className: activeHeading === heading.id ? 'active' : '', dangerouslySetInnerHTML: { __html: heading.value || '' }, "data-testid": `toc-${heading.value}`, onClick: (e) => {
67
67
  e.preventDefault();
68
- telemetry.sendTocItemClickedMessage();
68
+ telemetry.sendTocItemClickedMessage([(0, utils_1.getBaseDataAttributes)('tocItem', 'toc')]);
69
69
  handleHeadingClick(heading.id);
70
70
  } }));
71
71
  }))));
@@ -7,12 +7,13 @@ exports.LoginButton = LoginButton;
7
7
  const react_1 = __importDefault(require("react"));
8
8
  const hooks_1 = require("../../core/hooks");
9
9
  const Button_1 = require("../../components/Button/Button");
10
+ const utils_1 = require("../../core/utils");
10
11
  function LoginButton({ href, className, variant = 'primary', size = 'medium', label, labelTranslationKey = 'userMenu.login', 'data-component-name': componentName = 'UserMenu/LoginButton', }) {
11
12
  const { useTranslate, useTelemetry } = (0, hooks_1.useThemeHooks)();
12
13
  const { translate } = useTranslate();
13
14
  const telemetry = useTelemetry();
14
15
  const buttonLabel = label || translate(labelTranslationKey, 'Login');
15
16
  return (react_1.default.createElement("span", { "data-component-name": componentName, className: className },
16
- react_1.default.createElement(Button_1.Button, { "data-translation-key": label ? undefined : labelTranslationKey, to: href, languageInsensitive: true, onClick: () => telemetry.sendLoginButtonClickedMessage(), "data-testid": "login-btn", extraClass: className, variant: variant, size: size }, buttonLabel)));
17
+ react_1.default.createElement(Button_1.Button, { "data-translation-key": label ? undefined : labelTranslationKey, to: href, languageInsensitive: true, onClick: () => telemetry.sendLoginClickedMessage([(0, utils_1.getBaseDataAttributes)('login', 'button')]), "data-testid": "login-btn", extraClass: className, variant: variant, size: size }, buttonLabel)));
17
18
  }
18
19
  //# sourceMappingURL=LoginButton.js.map
@@ -8,13 +8,14 @@ const react_1 = __importDefault(require("react"));
8
8
  const hooks_1 = require("../../core/hooks");
9
9
  const LogoutIcon_1 = require("../../icons/LogoutIcon/LogoutIcon");
10
10
  const DropdownMenuItem_1 = require("../../components/Dropdown/DropdownMenuItem");
11
+ const utils_1 = require("../../core/utils");
11
12
  function LogoutMenuItem({ iconOnly, className }) {
12
13
  const { useTranslate, useTelemetry, useUserMenu } = (0, hooks_1.useThemeHooks)();
13
14
  const { handleLogout } = useUserMenu();
14
15
  const telemetry = useTelemetry();
15
16
  const { translate } = useTranslate();
16
17
  const handleClick = () => {
17
- telemetry.sendLogoutMenuItemClickedMessage();
18
+ telemetry.sendLogoutClickedMessage([(0, utils_1.getBaseDataAttributes)('logoutItem', 'userMenu')]);
18
19
  handleLogout();
19
20
  };
20
21
  return (react_1.default.createElement(DropdownMenuItem_1.DropdownMenuItem, { className: className, dangerous: true, "data-component-name": "UserMenu/LogoutMenuItem", onAction: handleClick, "data-translation-key": "userMenu.logout", prefix: react_1.default.createElement(LogoutIcon_1.LogoutIcon, null), style: { width: iconOnly ? 'fit-content' : '100%' } }, iconOnly ? null : translate('userMenu.logout', 'Log out')));
@@ -10,6 +10,7 @@ const hotkeys_js_1 = __importDefault(require("hotkeys-js"));
10
10
  const use_theme_hooks_1 = require("../use-theme-hooks");
11
11
  const use_theme_config_1 = require("../use-theme-config");
12
12
  const contexts_1 = require("../../contexts");
13
+ const utils_1 = require("../../utils");
13
14
  function useSearchDialog() {
14
15
  var _a;
15
16
  var _b;
@@ -25,7 +26,9 @@ function useSearchDialog() {
25
26
  if (hotKeys) {
26
27
  (0, hotkeys_js_1.default)(hotKeys, (ev) => {
27
28
  setIsOpen(true);
28
- telemetry.sendSearchOpenedMessage([{ object: 'search', method: 'shortcut' }]);
29
+ telemetry.sendSearchOpenedMessage([
30
+ Object.assign(Object.assign({}, (0, utils_1.getBaseDataAttributes)('searchDialogId', 'search')), { method: 'shortcut' }),
31
+ ]);
29
32
  ev.preventDefault();
30
33
  });
31
34
  return () => hotkeys_js_1.default.unbind(hotKeys);
@@ -33,7 +36,9 @@ function useSearchDialog() {
33
36
  // eslint-disable-next-line react-hooks/exhaustive-deps
34
37
  }, [hotKeys]);
35
38
  const onOpen = (0, react_1.useCallback)(function () {
36
- telemetry.sendSearchOpenedMessage([{ object: 'search', method: 'click' }]);
39
+ telemetry.sendSearchOpenedMessage([
40
+ Object.assign(Object.assign({}, (0, utils_1.getBaseDataAttributes)('searchDialogId', 'search')), { method: 'click' }),
41
+ ]);
37
42
  setIsOpen(true);
38
43
  // eslint-disable-next-line react-hooks/exhaustive-deps
39
44
  }, []);
@@ -14,6 +14,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
14
14
  exports.useBannerTelemetry = useBannerTelemetry;
15
15
  const react_1 = require("react");
16
16
  const use_theme_hooks_1 = require("./use-theme-hooks");
17
+ const get_base_data_attributes_1 = require("../utils/telemetry/get-base-data-attributes");
17
18
  const noop = () => { };
18
19
  const noopLink = (_href) => { };
19
20
  function useBannerTelemetry(displayBanner) {
@@ -27,17 +28,7 @@ function useBannerTelemetry(displayBanner) {
27
28
  sendBannerLinkClickedMessage: noopLink,
28
29
  };
29
30
  }
30
- const bannerUri = 'urn:redocly:realm:ui:banner:banner-id';
31
- const payload = {
32
- id: 'banner-id',
33
- object: 'banner',
34
- uri: bannerUri,
35
- trackingId: displayBanner.trackingId,
36
- hash: displayBanner.hash,
37
- color: displayBanner.color,
38
- target: displayBanner.target,
39
- dismissible: displayBanner.dismissible,
40
- };
31
+ const payload = Object.assign(Object.assign({}, (0, get_base_data_attributes_1.getBaseDataAttributes)('bannerId', 'banner')), { trackingId: displayBanner.trackingId, hash: displayBanner.hash, color: displayBanner.color, target: displayBanner.target, dismissible: displayBanner.dismissible });
41
32
  return {
42
33
  sendBannerViewedMessage: () => {
43
34
  telemetry.sendBannerViewedMessage([payload]);
@@ -7,6 +7,7 @@ const use_theme_config_1 = require("./use-theme-config");
7
7
  const use_theme_hooks_1 = require("./use-theme-hooks");
8
8
  const use_store_1 = require("./use-store");
9
9
  const constants_1 = require("../constants");
10
+ const utils_1 = require("../utils");
10
11
  const COLOR_MODE_KEY = 'colorSchema';
11
12
  const colorModeStore = (0, use_store_1.createStore)({
12
13
  storageKey: COLOR_MODE_KEY,
@@ -36,7 +37,7 @@ const useColorSwitcher = () => {
36
37
  root.classList.remove('notransition');
37
38
  });
38
39
  telemetry.sendColorModeSwitchedMessage([
39
- { object: 'color_mode', from: activeColorMode, to: newMode },
40
+ ...(0, utils_1.generateBeforeAfterContext)('colorMode', 'button', { mode: activeColorMode }, { mode: newMode }),
40
41
  ]);
41
42
  setActiveColorMode(newMode);
42
43
  };