@redocly/theme 0.56.0 → 0.57.0-next.1

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 (60) hide show
  1. package/lib/components/Catalog/CatalogEntities.js +1 -1
  2. package/lib/components/Dropdown/Dropdown.d.ts +2 -1
  3. package/lib/components/Dropdown/Dropdown.js +3 -2
  4. package/lib/components/Dropdown/variables.js +1 -1
  5. package/lib/core/hooks/__mocks__/search/use-recent-searches.d.ts +2 -2
  6. package/lib/core/hooks/__mocks__/search/use-recent-searches.js +2 -1
  7. package/lib/core/hooks/__mocks__/search/use-search-filter.d.ts +2 -2
  8. package/lib/core/hooks/__mocks__/search/use-search-filter.js +2 -1
  9. package/lib/core/hooks/__mocks__/search/use-suggested-pages.d.ts +2 -2
  10. package/lib/core/hooks/__mocks__/search/use-suggested-pages.js +2 -1
  11. package/lib/core/hooks/__mocks__/use-controlled-state.d.ts +1 -1
  12. package/lib/core/hooks/__mocks__/use-controlled-state.js +2 -1
  13. package/lib/core/hooks/__mocks__/use-input-key-commands.d.ts +3 -3
  14. package/lib/core/hooks/__mocks__/use-input-key-commands.js +3 -2
  15. package/lib/core/hooks/__mocks__/use-mobile-menu.d.ts +1 -1
  16. package/lib/core/hooks/__mocks__/use-mobile-menu.js +2 -1
  17. package/lib/core/hooks/__mocks__/use-theme-config.d.ts +2 -2
  18. package/lib/core/hooks/__mocks__/use-theme-config.js +2 -1
  19. package/lib/core/hooks/__mocks__/use-theme-hooks.d.ts +47 -47
  20. package/lib/core/hooks/__mocks__/use-theme-hooks.js +38 -37
  21. package/lib/core/hooks/__mocks__/use-time-ago.d.ts +2 -2
  22. package/lib/core/hooks/__mocks__/use-time-ago.js +2 -1
  23. package/lib/core/hooks/catalog/useCatalogEntities.js +6 -4
  24. package/lib/core/hooks/code-walkthrough/__mocks__/MockIntersectionObserver.js +4 -3
  25. package/lib/core/hooks/menu/__mocks__/use-mobile-menu-items.d.ts +1 -1
  26. package/lib/core/hooks/menu/__mocks__/use-mobile-menu-items.js +2 -1
  27. package/lib/core/hooks/use-page-actions.js +5 -1
  28. package/lib/core/types/l10n.d.ts +1 -1
  29. package/lib/core/types/user-claims.d.ts +2 -0
  30. package/lib/core/utils/dynamic.d.ts +7 -0
  31. package/lib/core/utils/dynamic.js +59 -0
  32. package/lib/core/utils/index.d.ts +1 -0
  33. package/lib/core/utils/index.js +1 -0
  34. package/lib/icons/GenericIcon/GenericIcon.d.ts +2 -2
  35. package/lib/icons/GenericIcon/GenericIcon.js +8 -3
  36. package/lib/icons/__tests__/IconTestUtils.d.ts +0 -1
  37. package/lib/icons/__tests__/IconTestUtils.js +8 -7
  38. package/package.json +11 -15
  39. package/src/components/Catalog/CatalogEntities.tsx +1 -1
  40. package/src/components/Dropdown/Dropdown.tsx +4 -1
  41. package/src/components/Dropdown/variables.ts +1 -1
  42. package/src/core/hooks/__mocks__/search/use-recent-searches.ts +3 -1
  43. package/src/core/hooks/__mocks__/search/use-search-filter.ts +3 -1
  44. package/src/core/hooks/__mocks__/search/use-suggested-pages.ts +3 -1
  45. package/src/core/hooks/__mocks__/use-controlled-state.ts +3 -1
  46. package/src/core/hooks/__mocks__/use-input-key-commands.ts +4 -2
  47. package/src/core/hooks/__mocks__/use-mobile-menu.ts +3 -1
  48. package/src/core/hooks/__mocks__/use-theme-config.ts +3 -1
  49. package/src/core/hooks/__mocks__/use-theme-hooks.ts +39 -37
  50. package/src/core/hooks/__mocks__/use-time-ago.ts +3 -1
  51. package/src/core/hooks/catalog/useCatalogEntities.ts +7 -4
  52. package/src/core/hooks/code-walkthrough/__mocks__/MockIntersectionObserver.ts +5 -3
  53. package/src/core/hooks/menu/__mocks__/use-mobile-menu-items.ts +3 -1
  54. package/src/core/hooks/use-page-actions.ts +5 -1
  55. package/src/core/types/l10n.ts +2 -0
  56. package/src/core/types/user-claims.ts +2 -0
  57. package/src/core/utils/dynamic.tsx +83 -0
  58. package/src/core/utils/index.ts +1 -0
  59. package/src/icons/GenericIcon/GenericIcon.tsx +13 -4
  60. package/src/icons/__tests__/IconTestUtils.tsx +5 -4
@@ -0,0 +1,59 @@
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.dynamic = dynamic;
7
+ const react_1 = __importDefault(require("react"));
8
+ function dynamic(factory, { ssr = true, loading } = {}) {
9
+ const Component = react_1.default.lazy(() => factory().then((mod) => {
10
+ // Case 1: factory() => import('./SomeModule') - direct import
11
+ // Case 2: factory() => import('./SomeModule').then(mod => mod.default) - extract default
12
+ if (typeof mod === 'function') {
13
+ return { default: mod };
14
+ }
15
+ // Case 3: factory() => import('./SomeModule').then(mod => ({ default: mod.default })) - manual wrap
16
+ // Case 4: factory() => import('./SomeModule').then(mod => ({ default: mod.Named })) - manual wrap named
17
+ if (mod && typeof mod === 'object' && 'default' in mod) {
18
+ const defaultExport = mod.default;
19
+ // Handle both function components and React component objects
20
+ if (typeof defaultExport === 'function') {
21
+ return { default: defaultExport };
22
+ }
23
+ if (defaultExport && typeof defaultExport === 'object' && '$$typeof' in defaultExport) {
24
+ return { default: defaultExport };
25
+ }
26
+ }
27
+ // Case 5: factory() => import('./SomeModule').then(mod => mod.Named) - find named export
28
+ if (mod && typeof mod === 'object') {
29
+ const modObj = mod;
30
+ for (const key in modObj) {
31
+ const value = modObj[key];
32
+ if (typeof value === 'function') {
33
+ return { default: value };
34
+ }
35
+ if (value && typeof value === 'object' && '$$typeof' in value) {
36
+ return { default: value };
37
+ }
38
+ }
39
+ }
40
+ throw new Error('Dynamic import did not return a valid React component');
41
+ }));
42
+ const LoadingComponent = loading || (() => null);
43
+ return (props) => {
44
+ const [hasMounted, setHasMounted] = react_1.default.useState(false);
45
+ react_1.default.useEffect(() => {
46
+ setHasMounted(true);
47
+ }, []);
48
+ if (ssr) {
49
+ return (react_1.default.createElement(react_1.default.Suspense, { fallback: react_1.default.createElement(LoadingComponent, Object.assign({}, props)) },
50
+ react_1.default.createElement(Component, Object.assign({}, props))));
51
+ }
52
+ if (!hasMounted) {
53
+ return react_1.default.createElement(LoadingComponent, Object.assign({}, props));
54
+ }
55
+ return (react_1.default.createElement(react_1.default.Suspense, { fallback: react_1.default.createElement(LoadingComponent, Object.assign({}, props)) },
56
+ react_1.default.createElement(Component, Object.assign({}, props))));
57
+ };
58
+ }
59
+ //# sourceMappingURL=dynamic.js.map
@@ -36,3 +36,4 @@ export * from './parse-style-string';
36
36
  export * from './lang-to-name';
37
37
  export * from './enhanced-smoothstep';
38
38
  export * from './icon-resolver';
39
+ export * from './dynamic';
@@ -52,4 +52,5 @@ __exportStar(require("./parse-style-string"), exports);
52
52
  __exportStar(require("./lang-to-name"), exports);
53
53
  __exportStar(require("./enhanced-smoothstep"), exports);
54
54
  __exportStar(require("./icon-resolver"), exports);
55
+ __exportStar(require("./dynamic"), exports);
55
56
  //# sourceMappingURL=index.js.map
@@ -1,5 +1,5 @@
1
1
  import * as React from 'react';
2
- export interface GenericIconProps {
2
+ export type GenericIconProps = {
3
3
  icon: string | React.ReactNode;
4
4
  srcSet?: string;
5
5
  rawContent?: string;
@@ -7,5 +7,5 @@ export interface GenericIconProps {
7
7
  color?: string;
8
8
  alt?: string;
9
9
  className?: string;
10
- }
10
+ };
11
11
  export declare function GenericIcon({ icon, srcSet, rawContent, size, color, alt, className, }: GenericIconProps): string | number | bigint | boolean | Iterable<React.ReactNode> | Promise<string | number | bigint | boolean | React.ReactPortal | React.ReactElement<unknown, string | React.JSXElementConstructor<any>> | Iterable<React.ReactNode> | null | undefined> | React.JSX.Element | null | undefined;
@@ -33,19 +33,24 @@ const CDNIcon_1 = require("../../icons/CDNIcon/CDNIcon");
33
33
  const utils_1 = require("../../core/utils");
34
34
  const InlineSvg_1 = require("../../markdoc/components/InlineSvg/InlineSvg");
35
35
  const Image_1 = require("../../components/Image/Image");
36
+ const INVALID_ICON_NAME = 'image-slash';
36
37
  function GenericIcon({ icon, srcSet, rawContent, size, color, alt, className, }) {
37
38
  if (srcSet) {
38
- return React.createElement(Image_1.Image, { srcSet: srcSet, alt: alt, className: className });
39
+ return React.createElement(IconSrcSetImg, { srcSet: srcSet, alt: alt, className: className });
39
40
  }
40
41
  const resolvedIcon = icon && typeof icon === 'string' ? (0, utils_1.resolveIcon)(icon) : null;
41
- const iconComponent = rawContent ? (React.createElement(IconSvg, { fileRawContent: rawContent, className: className })) : (resolvedIcon === null || resolvedIcon === void 0 ? void 0 : resolvedIcon.type) === 'link' ? (React.createElement(IconImg, { src: resolvedIcon.value, alt: alt, className: className })) : (resolvedIcon === null || resolvedIcon === void 0 ? void 0 : resolvedIcon.type) === 'font-awesome' ? (React.createElement(CDNIcon_1.CDNIcon, { name: resolvedIcon.name, type: resolvedIcon.style, size: size, color: color, className: className })) : (icon);
42
+ const iconComponent = rawContent ? (React.createElement(IconSvg, { fileRawContent: rawContent, className: className })) : (resolvedIcon === null || resolvedIcon === void 0 ? void 0 : resolvedIcon.type) === 'link' ? (React.createElement(IconImg, { src: resolvedIcon.value, alt: alt, className: className })) : (resolvedIcon === null || resolvedIcon === void 0 ? void 0 : resolvedIcon.type) === 'font-awesome' ? (React.createElement(CDNIcon_1.CDNIcon, { name: resolvedIcon.name, type: resolvedIcon.style, size: size, color: color, className: className })) : (resolvedIcon === null || resolvedIcon === void 0 ? void 0 : resolvedIcon.type) === 'invalid' ? (React.createElement(CDNIcon_1.CDNIcon, { name: INVALID_ICON_NAME, size: size, color: color, className: className })) : (icon);
42
43
  return iconComponent;
43
44
  }
44
45
  const IconImg = styled_components_1.default.img `
45
46
  width: var(--icon-width, 16px);
46
47
  height: var(--icon-height, 16px);
47
48
  display: inline-block;
48
- object-fit: cover;
49
+ vertical-align: middle;
50
+ `;
51
+ const IconSrcSetImg = (0, styled_components_1.default)(Image_1.Image) `
52
+ width: var(--icon-width, 16px);
53
+ height: var(--icon-height, 16px);
49
54
  `;
50
55
  const IconSvg = (0, styled_components_1.default)(InlineSvg_1.InlineSvg) `
51
56
  width: var(--icon-width, 16px);
@@ -1,5 +1,4 @@
1
1
  import React from 'react';
2
- import '@testing-library/jest-dom';
3
2
  export declare function testIconComponent(IconComponent: React.ComponentType<any>, componentName: string): {
4
3
  rendersCorrectlyWithDefaultProps: () => void;
5
4
  appliesCustomSizeAndColor: () => void;
@@ -6,42 +6,43 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.testIconComponent = testIconComponent;
7
7
  const react_1 = __importDefault(require("react"));
8
8
  const react_2 = require("@testing-library/react");
9
- require("@testing-library/jest-dom");
9
+ const vitest_1 = require("vitest");
10
10
  function testIconComponent(IconComponent, componentName) {
11
11
  const dataName = `icons/${componentName}/${componentName}`;
12
12
  return {
13
13
  rendersCorrectlyWithDefaultProps: () => {
14
14
  const { container } = (0, react_2.render)(react_1.default.createElement(IconComponent, null));
15
15
  const svgElement = container.querySelector('svg');
16
- expect(svgElement).toBeInTheDocument();
16
+ (0, vitest_1.expect)(svgElement).toBeTruthy();
17
17
  // Check if styles are applied either via style attribute or CSS class
18
18
  const element = container.firstChild;
19
19
  const hasStyleAttr = element.style.height === '16px' && element.style.width === '16px';
20
20
  const elementClass = element.getAttribute('class') || '';
21
21
  const hasClassName = elementClass.includes('sc-'); // styled-components class
22
- expect(hasStyleAttr || hasClassName).toBeTruthy();
22
+ (0, vitest_1.expect)(hasStyleAttr || hasClassName).toBeTruthy();
23
23
  },
24
24
  appliesCustomSizeAndColor: () => {
25
25
  var _a;
26
26
  const { container } = (0, react_2.render)(react_1.default.createElement(IconComponent, { size: "24px", color: "--color-primary" }));
27
27
  const svgElement = container.querySelector('svg');
28
- expect(svgElement).toBeInTheDocument();
28
+ (0, vitest_1.expect)(svgElement).toBeTruthy();
29
29
  const element = container.firstChild;
30
30
  const pathElement = container.querySelector('path');
31
31
  // Check if size styles are applied either via style attribute or CSS class
32
32
  const hasSizeStyle = element.style.height === '24px' && element.style.width === '24px';
33
33
  const elementClass = element.getAttribute('class') || '';
34
34
  const hasClassName = elementClass.includes('sc-'); // styled-components class
35
- expect(hasSizeStyle || hasClassName).toBeTruthy();
35
+ (0, vitest_1.expect)(hasSizeStyle || hasClassName).toBeTruthy();
36
36
  // Check if color is applied either via style attribute or CSS class
37
37
  const hasColorStyle = ((_a = pathElement.getAttribute('style')) === null || _a === void 0 ? void 0 : _a.includes('fill: var(--color-primary)')) ||
38
38
  pathElement.getAttribute('fill') === 'var(--color-primary)' ||
39
39
  elementClass.includes('sc-'); // check styled-components class on the root element
40
- expect(hasColorStyle).toBeTruthy();
40
+ (0, vitest_1.expect)(hasColorStyle).toBeTruthy();
41
41
  },
42
42
  hasCorrectDataComponentName: () => {
43
43
  const { container } = (0, react_2.render)(react_1.default.createElement(IconComponent, null));
44
- expect(container.firstChild).toHaveAttribute('data-component-name', dataName);
44
+ (0, vitest_1.expect)(container.firstChild).toHaveProperty('getAttribute');
45
+ (0, vitest_1.expect)(container.firstChild.getAttribute('data-component-name')).toBe(dataName);
45
46
  },
46
47
  };
47
48
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@redocly/theme",
3
- "version": "0.56.0",
3
+ "version": "0.57.0-next.1",
4
4
  "description": "Shared UI components lib",
5
5
  "keywords": [
6
6
  "theme",
@@ -38,13 +38,10 @@
38
38
  "devDependencies": {
39
39
  "@markdoc/markdoc": "0.5.2",
40
40
  "@shikijs/types": "1.24.2",
41
- "@testing-library/jest-dom": "6.6.3",
42
41
  "@testing-library/react": "16.3.0",
43
42
  "@testing-library/user-event": "14.6.1",
44
43
  "@types/file-saver": "2.0.7",
45
44
  "@types/highlight-words-core": "1.2.3",
46
- "@types/jest": "29.5.11",
47
- "@types/jest-when": "3.5.5",
48
45
  "@types/lodash.debounce": "4.0.9",
49
46
  "@types/lodash.throttle": "4.1.9",
50
47
  "@types/node": "22.15.3",
@@ -53,11 +50,10 @@
53
50
  "@types/react-dom": "^19.1.4",
54
51
  "@types/styled-components": "5.1.34",
55
52
  "@types/styled-system": "5.1.22",
53
+ "@vitest/coverage-v8": "^3.1.1",
54
+ "@vitest/ui": "3.2.4",
56
55
  "concurrently": "7.6.0",
57
- "jest": "29.5.0",
58
- "jest-environment-jsdom": "29.5.0",
59
- "jest-styled-components": "7.2.0",
60
- "jest-when": "3.6.0",
56
+ "vitest-when": "0.6.2",
61
57
  "json-schema-to-ts": "2.7.2",
62
58
  "npm-run-all2": "5.0.2",
63
59
  "react-refresh": "0.14.2",
@@ -66,13 +62,13 @@
66
62
  "rimraf": "5.0.7",
67
63
  "styled-components": "5.3.11",
68
64
  "styled-system": "5.1.5",
69
- "ts-jest": "29.1.2",
70
65
  "ts-node": "10.9.2",
71
66
  "ts-node-dev": "2.0.0",
72
67
  "tsc-alias": "1.8.10",
73
68
  "tsconfig-paths": "4.2.0",
74
69
  "tsconfig-paths-webpack-plugin": "3.5.2",
75
70
  "typescript": "5.6.2",
71
+ "vitest": "3.2.4",
76
72
  "webpack": "5.94.0"
77
73
  },
78
74
  "dependencies": {
@@ -90,7 +86,7 @@
90
86
  "react-calendar": "5.1.0",
91
87
  "react-date-picker": "11.0.0",
92
88
  "@redocly/config": "0.28.0",
93
- "@redocly/realm-asyncapi-sdk": "0.2.0"
89
+ "@redocly/realm-asyncapi-sdk": "0.3.0-next.0"
94
90
  },
95
91
  "scripts": {
96
92
  "watch": "tsc -p tsconfig.build.json && (concurrently \"tsc -w -p tsconfig.build.json\" \"tsc-alias -w -p tsconfig.build.json\")",
@@ -98,10 +94,10 @@
98
94
  "clean": "rimraf lib",
99
95
  "compile": "tsc -p tsconfig.build.json && tsc-alias -p tsconfig.build.json",
100
96
  "build": "npm run clean && npm run compile",
101
- "test": "jest",
102
- "test:update": "jest -u",
103
- "test:watch": "jest --watch",
104
- "test:coverage": "jest --coverage",
105
- "test:coverage:html": "jest --coverage --coverageReporters html"
97
+ "test": "vitest run",
98
+ "test:update": "vitest run --update",
99
+ "test:watch": "vitest",
100
+ "test:coverage": "vitest run --coverage",
101
+ "test:coverage:html": "vitest run --coverage --reporter=html"
106
102
  }
107
103
  }
@@ -58,7 +58,7 @@ export function CatalogEntities(props: CatalogEntitiesProps): JSX.Element {
58
58
  limit: LOAD_MORE_THRESHOLD,
59
59
  filter:
60
60
  initialFilter && filterQuery
61
- ? `${initialFilter} AND ${filterQuery}`
61
+ ? `(${initialFilter}) AND (${filterQuery})`
62
62
  : initialFilter || filterQuery,
63
63
  sort: sortOption || undefined,
64
64
  search: searchQuery,
@@ -29,6 +29,7 @@ export type DropdownProps = PropsWithChildren<{
29
29
  withArrow?: boolean;
30
30
 
31
31
  onClick?: (event: React.UIEvent) => void;
32
+ onClose?: () => void;
32
33
  }>;
33
34
 
34
35
  export function Dropdown({
@@ -43,6 +44,7 @@ export function Dropdown({
43
44
  placement,
44
45
  alignment,
45
46
  onClick,
47
+ onClose,
46
48
  }: DropdownProps): JSX.Element {
47
49
  const dropdownRef = useRef<HTMLDivElement | null>(null);
48
50
  const [isOpen, setIsOpen] = useControlledState<boolean>(false, active);
@@ -53,6 +55,7 @@ export function Dropdown({
53
55
 
54
56
  const handleClose = () => {
55
57
  setIsOpen(false);
58
+ onClose?.();
56
59
  };
57
60
 
58
61
  const handleChildClick = () => {
@@ -126,7 +129,7 @@ const DropdownWrapper = styled.div`
126
129
  `;
127
130
 
128
131
  const ChildrenWrapper = styled.div<{ placement?: string; alignment?: string; isOpen?: boolean }>`
129
- padding-top: var(--dropdown-menu-margin-top);
132
+ padding-top: var(--dropdown-menu-padding-top);
130
133
  position: absolute;
131
134
  top: ${({ placement }) => (placement === 'top' ? 'auto' : '100%')};
132
135
  bottom: ${({ placement }) => (placement === 'top' ? '100%' : 'auto')};
@@ -9,7 +9,7 @@ export const dropdown = css`
9
9
  --dropdown-menu-line-height: var(--line-height-base); // @presenter LineHeight
10
10
  --dropdown-menu-text-color: var(--text-color-secondary); // @presenter Color
11
11
 
12
- --dropdown-menu-margin-top: var(--spacing-xxs);
12
+ --dropdown-menu-padding-top: var(--spacing-xxs);
13
13
  --dropdown-menu-min-width: 100px;
14
14
  --dropdown-menu-max-width: 424px;
15
15
  --dropdown-menu-max-height: 300px;
@@ -1,4 +1,6 @@
1
- export const useRecentSearches = jest.fn(() => {
1
+ import { vi } from 'vitest';
2
+
3
+ export const useRecentSearches = vi.fn(() => {
2
4
  const items = ['test'];
3
5
  return {
4
6
  items,
@@ -1,4 +1,6 @@
1
- export const useSearchFilter = jest.fn(() => {
1
+ import { vi } from 'vitest';
2
+
3
+ export const useSearchFilter = vi.fn(() => {
2
4
  return {
3
5
  isFilterOpen: false,
4
6
  onFacetReset: () => {},
@@ -1,4 +1,6 @@
1
- export const useSuggestedPages = jest.fn(() => {
1
+ import { vi } from 'vitest';
2
+
3
+ export const useSuggestedPages = vi.fn(() => {
2
4
  return [
3
5
  {
4
6
  link: '/',
@@ -1 +1,3 @@
1
- export const useControlledState = () => [false, jest.fn()];
1
+ import { vi } from 'vitest';
2
+
3
+ export const useControlledState = () => [false, vi.fn()];
@@ -1,3 +1,5 @@
1
- export const useInputKeyCommands = jest.fn(() => ({
2
- onKeyDown: jest.fn(),
1
+ import { vi } from 'vitest';
2
+
3
+ export const useInputKeyCommands = vi.fn(() => ({
4
+ onKeyDown: vi.fn(),
3
5
  }));
@@ -1 +1,3 @@
1
- export const useMobileMenu = () => [false, jest.fn];
1
+ import { vi } from 'vitest';
2
+
3
+ export const useMobileMenu = () => [false, vi.fn()];
@@ -1,4 +1,6 @@
1
- export const useThemeConfig = jest.fn(() => ({
1
+ import { vi } from 'vitest';
2
+
3
+ export const useThemeConfig = vi.fn(() => ({
2
4
  userMenu: {
3
5
  items: [],
4
6
  },
@@ -1,6 +1,8 @@
1
- export const useThemeHooks = jest.fn(() => ({
2
- useTranslate: jest.fn(() => ({
3
- translate: jest.fn((key: string, defaultValue: string) =>
1
+ import { vi } from 'vitest';
2
+
3
+ export const useThemeHooks = vi.fn(() => ({
4
+ useTranslate: vi.fn(() => ({
5
+ translate: vi.fn((key: string, defaultValue: string) =>
4
6
  defaultValue
5
7
  ? typeof defaultValue !== 'string'
6
8
  ? (defaultValue as any).defaultValue
@@ -8,21 +10,21 @@ export const useThemeHooks = jest.fn(() => ({
8
10
  : key,
9
11
  ),
10
12
  })),
11
- useTelemetry: jest.fn(() => ({
12
- send: jest.fn(),
13
+ useTelemetry: vi.fn(() => ({
14
+ send: vi.fn(),
13
15
  })),
14
- useBreadcrumbs: jest.fn().mockReturnValue([]),
15
- usePageSharedData: jest.fn().mockReturnValue({}),
16
- useCatalogClassic: jest.fn(() => ({
16
+ useBreadcrumbs: vi.fn().mockReturnValue([]),
17
+ usePageSharedData: vi.fn().mockReturnValue({}),
18
+ useCatalogClassic: vi.fn(() => ({
17
19
  groups: [],
18
20
  filters: [],
19
21
  filterTerm: '',
20
- setFilterTerm: jest.fn(),
22
+ setFilterTerm: vi.fn(),
21
23
  })),
22
- useL10n: jest.fn(() => ({
23
- changeLanguage: jest.fn(),
24
+ useL10n: vi.fn(() => ({
25
+ changeLanguage: vi.fn(),
24
26
  })),
25
- useL10nConfig: jest.fn(() => ({
27
+ useL10nConfig: vi.fn(() => ({
26
28
  currentLocale: 'en',
27
29
  defaultLocale: 'en',
28
30
  locales: [
@@ -31,42 +33,42 @@ export const useThemeHooks = jest.fn(() => ({
31
33
  { code: 'uk', name: 'Ukraine' },
32
34
  ],
33
35
  })),
34
- usePreloadHistory: jest.fn(() => ({
35
- push: jest.fn(),
36
+ usePreloadHistory: vi.fn(() => ({
37
+ push: vi.fn(),
36
38
  })),
37
- useCurrentProduct: jest.fn(),
38
- useProducts: jest.fn(() => []),
39
- useSearch: jest.fn(() => ({
40
- query: jest.fn().mockReturnValue(''),
41
- setQuery: jest.fn(),
39
+ useCurrentProduct: vi.fn(),
40
+ useProducts: vi.fn(() => []),
41
+ useSearch: vi.fn(() => ({
42
+ query: vi.fn().mockReturnValue(''),
43
+ setQuery: vi.fn(),
42
44
  items: [],
43
45
  isLoading: false,
44
46
  })),
45
- useAiSearch: jest.fn(() => ({
46
- askQuestion: jest.fn(),
47
+ useAiSearch: vi.fn(() => ({
48
+ askQuestion: vi.fn(),
47
49
  references: [],
48
50
  })),
49
- useFacetQuery: jest.fn(() => ({
51
+ useFacetQuery: vi.fn(() => ({
50
52
  searchFacet: null,
51
- setSearchFacet: jest.fn(),
52
- setSearchFacetQuery: jest.fn(),
53
+ setSearchFacet: vi.fn(),
54
+ setSearchFacetQuery: vi.fn(),
53
55
  })),
54
- useMarkdownText: jest.fn(() => {
56
+ useMarkdownText: vi.fn(() => {
55
57
  return null;
56
58
  }),
57
- useUserMenu: jest.fn(() => ({
59
+ useUserMenu: vi.fn(() => ({
58
60
  userData: {
59
61
  isAuthenticated: true,
60
62
  name: 'test-name',
61
63
  email: 'test-email@test.xyz',
62
64
  picture: 'picture',
63
65
  },
64
- handleLogout: jest.fn(),
66
+ handleLogout: vi.fn(),
65
67
  loginUrl: 'https://login.redoc.ly',
66
68
  hasDeveloperOnboarding: true,
67
69
  })),
68
- usePageVersions: jest.fn(),
69
- useSidebarItems: jest.fn().mockReturnValue({
70
+ usePageVersions: vi.fn(),
71
+ useSidebarItems: vi.fn().mockReturnValue({
70
72
  currentItems: [
71
73
  {
72
74
  label: 'Item',
@@ -77,14 +79,14 @@ export const useThemeHooks = jest.fn(() => ({
77
79
  label: 'Item',
78
80
  },
79
81
  ],
80
- popDrilldownState: jest.fn(),
81
- pushDrilldownState: jest.fn(),
82
+ popDrilldownState: vi.fn(),
83
+ pushDrilldownState: vi.fn(),
82
84
  }),
83
- useSidebarSiblingsData: jest.fn(),
84
- useSubmitFeedback: jest.fn(() => ({
85
- submitFeedback: jest.fn(),
85
+ useSidebarSiblingsData: vi.fn(),
86
+ useSubmitFeedback: vi.fn(() => ({
87
+ submitFeedback: vi.fn(),
86
88
  })),
87
- useLoadAndNavigate: jest.fn(),
88
- usePageProps: jest.fn().mockReturnValue({}),
89
- usePageData: jest.fn().mockReturnValue(null),
89
+ useLoadAndNavigate: vi.fn(),
90
+ usePageProps: vi.fn().mockReturnValue({}),
91
+ usePageData: vi.fn().mockReturnValue(null),
90
92
  }));
@@ -1,3 +1,5 @@
1
- export const useTimeAgo = jest.fn(() => ({
1
+ import { vi } from 'vitest';
2
+
3
+ export const useTimeAgo = vi.fn(() => ({
2
4
  format: () => '5 days ago',
3
5
  }));
@@ -6,11 +6,14 @@ export function useCatalogEntities({ entitiesTypes, excludedEntities }: UseCatal
6
6
  const initialExcludedEntitiesFilter = excludedEntities?.length
7
7
  ? `-key:${excludedEntities.map((entity) => entity.key).join(',')}`
8
8
  : '';
9
- const initialFilter = initialTypesFilter
10
- ? `${initialTypesFilter} AND ${initialExcludedEntitiesFilter}`
11
- : initialExcludedEntitiesFilter;
9
+
10
+ if (initialTypesFilter && initialExcludedEntitiesFilter) {
11
+ return {
12
+ initialFilter: `(${initialTypesFilter}) AND (${initialExcludedEntitiesFilter})`,
13
+ };
14
+ }
12
15
 
13
16
  return {
14
- initialFilter,
17
+ initialFilter: initialTypesFilter || initialExcludedEntitiesFilter,
15
18
  };
16
19
  }
@@ -1,3 +1,5 @@
1
+ import { vi } from 'vitest';
2
+
1
3
  export class MockIntersectionObserver {
2
4
  public readonly root: Element | Document | null;
3
5
  public readonly rootMargin: string;
@@ -11,9 +13,9 @@ export class MockIntersectionObserver {
11
13
  this.root = null;
12
14
  this.rootMargin = '0px';
13
15
  this.thresholds = [1];
14
- this.observe = jest.fn;
15
- this.unobserve = jest.fn;
16
- this.disconnect = jest.fn;
16
+ this.observe = vi.fn();
17
+ this.unobserve = vi.fn();
18
+ this.disconnect = vi.fn();
17
19
  }
18
20
 
19
21
  takeRecords(): any[] {
@@ -1,4 +1,6 @@
1
- export const useMobileMenuItems = jest.fn().mockReturnValue({
1
+ import { vi } from 'vitest';
2
+
3
+ export const useMobileMenuItems = vi.fn().mockReturnValue({
2
4
  items: [
3
5
  {
4
6
  type: 'group',
@@ -20,7 +20,8 @@ export function usePageActions(pageSlug: string): PageAction[] {
20
20
 
21
21
  const { navigation } = useThemeConfig();
22
22
  // Can't use any actions if no markdown files are generated for LLMs
23
- const isLlmsDisabled = usePageProps()?.seo?.llmstxt?.hide === true;
23
+ const pageProps = usePageProps();
24
+ const isLlmsDisabled = pageProps?.seo?.llmstxt?.hide || pageProps?.frontmatter?.excludeFromSearch;
24
25
  const { isPublic } = usePageData() || {};
25
26
 
26
27
  const actions: PageAction[] = useMemo(() => {
@@ -60,6 +61,9 @@ export function usePageActions(pageSlug: string): PageAction[] {
60
61
  onClick: async () => {
61
62
  try {
62
63
  const result = await fetch(mdPageUrl);
64
+ if (result.status !== 200) {
65
+ throw new Error('Failed to fetch markdown content');
66
+ }
63
67
  const text = await result.text();
64
68
 
65
69
  ClipboardService.copyCustom(text);
@@ -257,6 +257,8 @@ export type TranslationKey =
257
257
  | 'openapi.collapseAll'
258
258
  | 'openapi.viewSecurityDetails'
259
259
  | 'openapi.noResponseExample'
260
+ | 'openapi.discriminator.searchPlaceholder'
261
+ | 'openapi.discriminator.searchNoResults'
260
262
  | 'openapi.noResponseContent'
261
263
  | 'openapi.noRequestPayload'
262
264
  | 'openapi.hidePattern'
@@ -1,4 +1,6 @@
1
1
  export type UserClaims = {
2
2
  email?: string;
3
3
  name?: string;
4
+ federatedAccessToken?: string;
5
+ federatedIdToken?: string;
4
6
  };