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

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 (86) 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/constants/search.js +4 -0
  26. package/lib/core/hooks/search/use-search-dialog.js +7 -2
  27. package/lib/core/hooks/use-banner-telemetry.js +2 -11
  28. package/lib/core/hooks/use-color-switcher.js +2 -1
  29. package/lib/core/hooks/use-page-actions.js +29 -36
  30. package/lib/core/hooks/use-product-picker.js +14 -4
  31. package/lib/core/hooks/use-telemetry-fallback.d.ts +45 -37
  32. package/lib/core/hooks/use-telemetry-fallback.js +47 -39
  33. package/lib/core/styles/global.js +33 -0
  34. package/lib/core/types/l10n.d.ts +1 -1
  35. package/lib/core/types/search.d.ts +2 -1
  36. package/lib/core/types/search.js +1 -0
  37. package/lib/core/utils/index.d.ts +2 -0
  38. package/lib/core/utils/index.js +2 -0
  39. package/lib/core/utils/telemetry/generate-before-after-context.d.ts +37 -0
  40. package/lib/core/utils/telemetry/generate-before-after-context.js +32 -0
  41. package/lib/core/utils/telemetry/generate-resource-urn.d.ts +7 -0
  42. package/lib/core/utils/telemetry/generate-resource-urn.js +13 -0
  43. package/lib/core/utils/telemetry/get-base-data-attributes.d.ts +14 -0
  44. package/lib/core/utils/telemetry/get-base-data-attributes.js +17 -0
  45. package/lib/layouts/McpNotAllowed.d.ts +2 -0
  46. package/lib/layouts/McpNotAllowed.js +67 -0
  47. package/package.json +3 -3
  48. package/src/components/Breadcrumbs/BreadcrumbDropdown.tsx +2 -1
  49. package/src/components/Breadcrumbs/Breadcrumbs.tsx +4 -4
  50. package/src/components/Buttons/AIAssistantButton.tsx +2 -3
  51. package/src/components/Buttons/EditPageButton.tsx +4 -1
  52. package/src/components/Catalog/CatalogCardView/CatalogCard.tsx +2 -2
  53. package/src/components/CatalogClassic/CatalogClassicActions.tsx +4 -2
  54. package/src/components/CatalogClassic/CatalogClassicCard.tsx +9 -2
  55. package/src/components/CatalogClassic/CatalogClassicInfoBlock.tsx +2 -1
  56. package/src/components/Feedback/ReportDialog.tsx +4 -1
  57. package/src/components/Filter/FilterCheckboxes.tsx +4 -2
  58. package/src/components/Footer/FooterItem.tsx +4 -2
  59. package/src/components/LanguagePicker/LanguagePicker.tsx +9 -2
  60. package/src/components/Logo/Logo.tsx +7 -1
  61. package/src/components/Menu/MenuContainer.tsx +1 -0
  62. package/src/components/Menu/MenuItem.tsx +35 -4
  63. package/src/components/Navbar/Navbar.tsx +7 -3
  64. package/src/components/Navbar/NavbarItem.tsx +7 -1
  65. package/src/components/Search/SearchDialog.tsx +6 -13
  66. package/src/components/Search/SearchInput.tsx +4 -1
  67. package/src/components/Search/SearchRecent.tsx +4 -2
  68. package/src/components/SidebarActions/SidebarActions.tsx +10 -3
  69. package/src/components/TableOfContent/TableOfContent.tsx +7 -2
  70. package/src/components/UserMenu/LoginButton.tsx +4 -1
  71. package/src/components/UserMenu/LogoutMenuItem.tsx +2 -1
  72. package/src/core/constants/search.ts +4 -0
  73. package/src/core/hooks/search/use-search-dialog.ts +13 -2
  74. package/src/core/hooks/use-banner-telemetry.ts +5 -4
  75. package/src/core/hooks/use-color-switcher.ts +7 -1
  76. package/src/core/hooks/use-page-actions.ts +38 -44
  77. package/src/core/hooks/use-product-picker.ts +19 -5
  78. package/src/core/hooks/use-telemetry-fallback.ts +47 -39
  79. package/src/core/styles/global.ts +33 -0
  80. package/src/core/types/l10n.ts +2 -0
  81. package/src/core/types/search.ts +1 -0
  82. package/src/core/utils/index.ts +2 -0
  83. package/src/core/utils/telemetry/generate-before-after-context.ts +59 -0
  84. package/src/core/utils/telemetry/generate-resource-urn.ts +9 -0
  85. package/src/core/utils/telemetry/get-base-data-attributes.ts +27 -0
  86. package/src/layouts/McpNotAllowed.tsx +81 -0
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.generateResourceUrn = generateResourceUrn;
4
+ /**
5
+ * Generates a resource URN for Realm UI telemetry.
6
+ * @param object - The object type.
7
+ * @param id - The resource ID.
8
+ * @returns The resource URN.
9
+ */
10
+ function generateResourceUrn(object, id) {
11
+ return `urn:redocly:realm:ui:${object}:${id}`;
12
+ }
13
+ //# sourceMappingURL=generate-resource-urn.js.map
@@ -0,0 +1,14 @@
1
+ export type BaseDataAttributes<T, K, U> = {
2
+ /** Resource identifier (e.g. UI key, or page URL when tracking navigation). */
3
+ id: T;
4
+ /** Object kind used for telemetry categorization (e.g. `'button'`, `'page'`). */
5
+ object: K;
6
+ /** Resolved URI: the optional `url` when provided, otherwise a Realm UI resource URN. */
7
+ uri: U;
8
+ };
9
+ /**
10
+ * Builds the base fields for Realm UI telemetry payloads: `id`, `object`, and `uri`.
11
+ * When `url` is omitted, {@link generateResourceUrn} derives `uri` as
12
+ * `urn:redocly:realm:ui:{object}:{id}`.
13
+ */
14
+ export declare function getBaseDataAttributes<T, K, U>(id: T, object: K, url?: U): BaseDataAttributes<T, K, U>;
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getBaseDataAttributes = getBaseDataAttributes;
4
+ const generate_resource_urn_1 = require("./generate-resource-urn");
5
+ /**
6
+ * Builds the base fields for Realm UI telemetry payloads: `id`, `object`, and `uri`.
7
+ * When `url` is omitted, {@link generateResourceUrn} derives `uri` as
8
+ * `urn:redocly:realm:ui:{object}:{id}`.
9
+ */
10
+ function getBaseDataAttributes(id, object, url) {
11
+ return {
12
+ id,
13
+ object,
14
+ uri: url !== null && url !== void 0 ? url : (0, generate_resource_urn_1.generateResourceUrn)(object, id),
15
+ };
16
+ }
17
+ //# sourceMappingURL=get-base-data-attributes.js.map
@@ -0,0 +1,2 @@
1
+ import type { JSX } from 'react';
2
+ export default function McpNotAllowed(): JSX.Element;
@@ -0,0 +1,67 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.default = McpNotAllowed;
7
+ const react_1 = __importDefault(require("react"));
8
+ const styled_components_1 = __importDefault(require("styled-components"));
9
+ const hooks_1 = require("../core/hooks");
10
+ const ConnectMCPButton_1 = require("../components/Buttons/ConnectMCPButton");
11
+ function McpNotAllowed() {
12
+ const { useTranslate } = (0, hooks_1.useThemeHooks)();
13
+ const { translate } = useTranslate();
14
+ return (react_1.default.createElement(NotAllowedWrapper, { "data-component-name": "layouts/McpNotAllowed" },
15
+ react_1.default.createElement(StatusText, null, "405"),
16
+ react_1.default.createElement(Title, { "data-translation-key": "page.mcpNotAllowed.title" }, translate('page.mcpNotAllowed.title', 'Method not allowed')),
17
+ react_1.default.createElement(Description, { "data-translation-key": "page.mcpNotAllowed.description" }, translate('page.mcpNotAllowed.description', 'To use this MCP server, register it in your MCP Client (for example: VS Code, Cursor, or Claude Code).')),
18
+ react_1.default.createElement(ButtonWrapper, null,
19
+ react_1.default.createElement(ConnectMCPButton_1.ConnectMCPButton, { alignment: "start" }))));
20
+ }
21
+ const NotAllowedWrapper = styled_components_1.default.div `
22
+ height: 100%;
23
+ max-width: var(--page-mcp-405-max-width);
24
+ display: flex;
25
+ flex-direction: column;
26
+ justify-content: center;
27
+ margin: var(--page-mcp-405-margin-vertical) var(--page-mcp-405-margin-horizontal);
28
+ font-family: var(--page-mcp-405-font-family);
29
+ gap: var(--page-mcp-405-gap);
30
+
31
+ @media (max-width: 480px) {
32
+ margin: var(--page-mcp-405-margin-vertical-mobile) var(--page-mcp-405-margin-horizontal-mobile);
33
+ }
34
+ `;
35
+ const StatusText = styled_components_1.default.div `
36
+ color: var(--page-mcp-405-status-text-color);
37
+ font-size: var(--page-mcp-405-status-font-size);
38
+ line-height: var(--page-mcp-405-status-line-height);
39
+ font-weight: var(--page-mcp-405-status-font-weight);
40
+ `;
41
+ const Title = styled_components_1.default.div `
42
+ font-size: var(--page-mcp-405-title-font-size);
43
+ line-height: var(--page-mcp-405-title-line-height);
44
+ font-weight: var(--page-mcp-405-title-font-weight);
45
+ color: var(--page-mcp-405-title-text-color);
46
+
47
+ @media (max-width: 480px) {
48
+ font-size: var(--page-mcp-405-title-font-size-mobile);
49
+ line-height: var(--page-mcp-405-title-line-height-mobile);
50
+ }
51
+ `;
52
+ const Description = styled_components_1.default.div `
53
+ color: var(--page-mcp-405-description-text-color);
54
+ font-size: var(--page-mcp-405-description-font-size);
55
+ line-height: var(--page-mcp-405-description-line-height);
56
+ font-weight: var(--page-mcp-405-description-font-weight);
57
+
58
+ @media (max-width: 480px) {
59
+ font-size: var(--page-mcp-405-description-font-size-mobile);
60
+ line-height: var(--page-mcp-405-description-line-height-mobile);
61
+ }
62
+ `;
63
+ const ButtonWrapper = styled_components_1.default.div `
64
+ position: relative;
65
+ display: inline-block;
66
+ `;
67
+ //# sourceMappingURL=McpNotAllowed.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@redocly/theme",
3
- "version": "0.67.0-next.4",
3
+ "version": "0.67.0-next.6",
4
4
  "description": "Shared UI components lib",
5
5
  "keywords": [
6
6
  "theme",
@@ -63,7 +63,7 @@
63
63
  "vitest": "4.1.8",
64
64
  "vitest-when": "0.6.2",
65
65
  "webpack": "5.105.2",
66
- "@redocly/realm-asyncapi-sdk": "0.13.0-next.3"
66
+ "@redocly/realm-asyncapi-sdk": "0.13.0-next.4"
67
67
  },
68
68
  "dependencies": {
69
69
  "@tanstack/react-query": "5.62.3",
@@ -81,7 +81,7 @@
81
81
  "openapi-sampler": "^1.7.2",
82
82
  "react-calendar": "5.1.0",
83
83
  "react-date-picker": "11.0.0",
84
- "@redocly/config": "0.49.0"
84
+ "@redocly/config": "0.50.1"
85
85
  },
86
86
  "scripts": {
87
87
  "watch": "tsgo -p tsconfig.build.json && (concurrently \"tsgo -w -p tsconfig.build.json\" \"tsc-alias -w -p tsconfig.build.json\")",
@@ -12,6 +12,7 @@ import { Tooltip } from '@redocly/theme/components/Tooltip/Tooltip';
12
12
  import { GenericIcon } from '@redocly/theme/icons/GenericIcon/GenericIcon';
13
13
  import { BREADCRUMB_MAX_LENGTH } from '@redocly/theme/core/constants';
14
14
  import { BreadcrumbIcon } from '@redocly/theme/components/Breadcrumbs/BreadcrumbIcon';
15
+ import { getBaseDataAttributes } from '@redocly/theme/core/utils';
15
16
 
16
17
  type BreadcrumbDropdownProps = {
17
18
  children?: React.ReactNode;
@@ -81,7 +82,7 @@ export function BreadcrumbDropdown({
81
82
  onItemClick?.(item, index);
82
83
  telemetry.sendBreadcrumbClickedMessage([
83
84
  {
84
- object: 'breadcrumb',
85
+ ...getBaseDataAttributes('breadcrumbId', 'breadcrumb'),
85
86
  link: item.link,
86
87
  position: index + 1,
87
88
  totalBreadcrumbs: items.length,
@@ -8,7 +8,7 @@ import { useThemeHooks } from '@redocly/theme/core/hooks';
8
8
  import { Breadcrumb } from '@redocly/theme/components/Breadcrumbs/Breadcrumb';
9
9
  import { BreadcrumbDropdown } from '@redocly/theme/components/Breadcrumbs/BreadcrumbDropdown';
10
10
  import { BreadcrumbIcon } from '@redocly/theme/components/Breadcrumbs/BreadcrumbIcon';
11
- import { trimText } from '@redocly/theme/core/utils';
11
+ import { trimText, getBaseDataAttributes } from '@redocly/theme/core/utils';
12
12
  import { BREADCRUMB_MAX_LENGTH } from '@redocly/theme/core/constants';
13
13
 
14
14
  export function Breadcrumbs(props: {
@@ -68,7 +68,7 @@ export function Breadcrumbs(props: {
68
68
  onItemClick={(item, itemIdx) =>
69
69
  telemetry.sendBreadcrumbClickedMessage([
70
70
  {
71
- object: 'breadcrumb',
71
+ ...getBaseDataAttributes('breadcrumbId', 'breadcrumb'),
72
72
  link: item.link,
73
73
  position: itemIdx + 1,
74
74
  totalBreadcrumbs: siblingsWithActive.length,
@@ -91,7 +91,7 @@ export function Breadcrumbs(props: {
91
91
  onClick={() =>
92
92
  telemetry.sendBreadcrumbClickedMessage([
93
93
  {
94
- object: 'breadcrumb',
94
+ ...getBaseDataAttributes('breadcrumbId', 'breadcrumb'),
95
95
  link: breadcrumb.link,
96
96
  position: idx + 1,
97
97
  totalBreadcrumbs: breadcrumbs.length,
@@ -119,7 +119,7 @@ export function Breadcrumbs(props: {
119
119
  onItemClick={(item, itemIdx) =>
120
120
  telemetry.sendBreadcrumbClickedMessage([
121
121
  {
122
- object: 'breadcrumb',
122
+ ...getBaseDataAttributes('breadcrumbId', 'breadcrumb'),
123
123
  link: item.link,
124
124
  position: itemIdx + 1,
125
125
  totalBreadcrumbs: breadcrumbs.length,
@@ -9,6 +9,7 @@ import { ChatIcon } from '@redocly/theme/icons/ChatIcon/ChatIcon';
9
9
  import { AiStarsGradientIcon } from '@redocly/theme/icons/AiStarsGradientIcon/AiStarsGradientIcon';
10
10
  import { RedoclyIcon } from '@redocly/theme/icons/RedoclyIcon/RedoclyIcon';
11
11
  import { SearchSessionProvider } from '@redocly/theme/core/contexts';
12
+ import { getBaseDataAttributes } from '@redocly/theme/core/utils';
12
13
 
13
14
  type AIAssistantButtonIconType = 'chat' | 'sparkles' | 'redocly';
14
15
  type AIAssistantButtonType = 'button' | 'icon';
@@ -79,9 +80,7 @@ export function AIAssistantButton() {
79
80
  setIsOpen(true);
80
81
  telemetry.sendSearchAiOpenedMessage([
81
82
  {
82
- id: 'aiAssistantTriggerButton',
83
- object: 'search',
84
- uri: 'urn:redocly:realm:ui:search:aiAssistantTriggerButton',
83
+ ...getBaseDataAttributes('aiAssistantTriggerButton', 'search'),
85
84
  method: 'ai_trigger_button',
86
85
  },
87
86
  ]);
@@ -6,6 +6,7 @@ import type { JSX } from 'react';
6
6
  import { useThemeHooks } from '@redocly/theme/core/hooks';
7
7
  import { EditIcon } from '@redocly/theme/icons/EditIcon/EditIcon';
8
8
  import { Button } from '@redocly/theme/components/Button/Button';
9
+ import { getBaseDataAttributes } from '@redocly/theme/core/utils';
9
10
 
10
11
  export type EditPageButtonProps = {
11
12
  to: string;
@@ -24,7 +25,9 @@ export function EditPageButton({ to }: EditPageButtonProps): JSX.Element {
24
25
  variant="ghost"
25
26
  size="small"
26
27
  icon={<EditIcon />}
27
- onClick={() => telemetry.sendEditPageLinkClickedMessage()}
28
+ onClick={() =>
29
+ telemetry.sendPageEditClickedMessage([getBaseDataAttributes('pageEdit', 'button')])
30
+ }
28
31
  data-translation-key="markdown.editPage.text"
29
32
  >
30
33
  {translate('markdown.editPage.text', 'Edit')}
@@ -11,7 +11,7 @@ import { ArrowRightIcon } from '@redocly/theme/icons/ArrowRightIcon/ArrowRightIc
11
11
  import { ArrowUpRightIcon } from '@redocly/theme/icons/ArrowUpRightIcon/ArrowUpRightIcon';
12
12
  import { CatalogTags } from '@redocly/theme/components/Catalog/CatalogTags';
13
13
  import { CatalogEntityIcon } from '@redocly/theme/components/Catalog/CatalogEntityIcon';
14
- import { getPathPrefix } from '@redocly/theme/core/utils';
14
+ import { getPathPrefix, getBaseDataAttributes } from '@redocly/theme/core/utils';
15
15
 
16
16
  export type CatalogCardProps = {
17
17
  entity: BffCatalogEntity;
@@ -31,7 +31,7 @@ export function CatalogCard({ entity, catalogConfig }: CatalogCardProps): JSX.El
31
31
  window.location.assign(
32
32
  `${pathPrefix}/catalogs/${catalogConfig.slug}/entities/${entity.key}`,
33
33
  );
34
- telemetry.sendCatalogItemClickedMessage();
34
+ telemetry.sendCatalogItemClickedMessage([getBaseDataAttributes('catalogItem', 'card')]);
35
35
  }}
36
36
  >
37
37
  <CardContent>
@@ -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 (