@tomehq/theme 0.3.1 → 0.3.3
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.
- package/CHANGELOG.md +17 -0
- package/dist/{chunk-YXKONM3A.js → chunk-MSXVVBDW.js} +493 -143
- package/dist/entry.js +1 -1
- package/dist/index.d.ts +37 -1
- package/dist/index.js +1 -1
- package/package.json +5 -5
- package/src/Shell.test.tsx +405 -0
- package/src/Shell.tsx +248 -24
- package/src/__virtual_stubs/config.ts +2 -0
- package/src/__virtual_stubs/doc-context.ts +2 -0
- package/src/__virtual_stubs/overrides.ts +2 -0
- package/src/__virtual_stubs/page-loader.ts +4 -0
- package/src/__virtual_stubs/routes.ts +5 -0
- package/src/entry-helpers.test.ts +76 -0
- package/src/entry-helpers.ts +18 -1
- package/src/entry.test.tsx +695 -0
- package/src/entry.tsx +179 -4
- package/src/global.d.ts +11 -0
- package/vitest.config.ts +31 -1
- package/dist/chunk-2APCPR2Y.js +0 -2110
- package/dist/chunk-37JI6XGT.js +0 -1720
- package/dist/chunk-3A2LPGUL.js +0 -1991
- package/dist/chunk-3I2QTWTW.js +0 -1948
- package/dist/chunk-45M5UIAB.js +0 -2110
- package/dist/chunk-462AGU3S.js +0 -1959
- package/dist/chunk-7MUTU5D4.js +0 -1720
- package/dist/chunk-ABNPB6BB.js +0 -2133
- package/dist/chunk-BZGWSKT2.js +0 -573
- package/dist/chunk-CMQCNCSY.js +0 -2127
- package/dist/chunk-CTPOZMMK.js +0 -1703
- package/dist/chunk-DO544M3G.js +0 -1702
- package/dist/chunk-DPKZBFQP.js +0 -1777
- package/dist/chunk-EK7PZUEB.js +0 -2147
- package/dist/chunk-FMOLIHQF.js +0 -2182
- package/dist/chunk-FWBTK5TL.js +0 -1444
- package/dist/chunk-GDQIBNX5.js +0 -1962
- package/dist/chunk-GHQ2MODM.js +0 -2127
- package/dist/chunk-GR2WCRGK.js +0 -2182
- package/dist/chunk-HNLKDQ64.js +0 -2139
- package/dist/chunk-INUMUXN5.js +0 -2095
- package/dist/chunk-IW3NHNOQ.js +0 -2187
- package/dist/chunk-JA4PMX6M.js +0 -1500
- package/dist/chunk-JSPFS7G5.js +0 -2102
- package/dist/chunk-JZRT4WNC.js +0 -1441
- package/dist/chunk-KQBY2JDB.js +0 -2112
- package/dist/chunk-LIMYFTPC.js +0 -1468
- package/dist/chunk-MEP7P6A7.js +0 -1500
- package/dist/chunk-NOZBIES7.js +0 -1948
- package/dist/chunk-O4GH3KYX.js +0 -1712
- package/dist/chunk-OEXM3BEC.js +0 -1702
- package/dist/chunk-Q7PYTVW3.js +0 -1771
- package/dist/chunk-QCWZYABW.js +0 -1468
- package/dist/chunk-RDF25WB2.js +0 -2085
- package/dist/chunk-RKTT3ZEX.js +0 -1500
- package/dist/chunk-S47BRMNQ.js +0 -1715
- package/dist/chunk-S4ZH5F56.js +0 -1949
- package/dist/chunk-SRD7NJHS.js +0 -1949
- package/dist/chunk-SWFYJO5H.js +0 -2187
- package/dist/chunk-TQDWPSTO.js +0 -2087
- package/dist/chunk-TTRXRPP6.js +0 -1941
- package/dist/chunk-UKYFJSUA.js +0 -509
- package/dist/chunk-VKEQHP2E.js +0 -2133
- package/dist/chunk-VUT2FMSI.js +0 -1937
- package/dist/chunk-VVCC5JHK.js +0 -1949
- package/dist/chunk-W732TVBK.js +0 -1944
- package/dist/chunk-X4VQYPKO.js +0 -1768
- package/dist/chunk-YZ3P3TNS.js +0 -1760
package/dist/entry.js
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -10,6 +10,7 @@ interface I18nInfo {
|
|
|
10
10
|
defaultLocale: string;
|
|
11
11
|
locales: string[];
|
|
12
12
|
localeNames?: Record<string, string>;
|
|
13
|
+
localeDirs?: Record<string, "ltr" | "rtl">;
|
|
13
14
|
}
|
|
14
15
|
interface ShellProps {
|
|
15
16
|
config: {
|
|
@@ -49,6 +50,12 @@ interface ShellProps {
|
|
|
49
50
|
link?: string;
|
|
50
51
|
dismissible?: boolean;
|
|
51
52
|
};
|
|
53
|
+
socialLinks?: Array<{
|
|
54
|
+
platform: string;
|
|
55
|
+
url: string;
|
|
56
|
+
label?: string;
|
|
57
|
+
icon?: string;
|
|
58
|
+
}>;
|
|
52
59
|
[key: string]: unknown;
|
|
53
60
|
};
|
|
54
61
|
navigation: Array<{
|
|
@@ -58,6 +65,10 @@ interface ShellProps {
|
|
|
58
65
|
id: string;
|
|
59
66
|
urlPath: string;
|
|
60
67
|
icon?: string;
|
|
68
|
+
badge?: {
|
|
69
|
+
text: string;
|
|
70
|
+
variant: string;
|
|
71
|
+
};
|
|
61
72
|
}>;
|
|
62
73
|
}>;
|
|
63
74
|
currentPageId: string;
|
|
@@ -85,6 +96,22 @@ interface ShellProps {
|
|
|
85
96
|
items: string[];
|
|
86
97
|
}>;
|
|
87
98
|
}>;
|
|
99
|
+
apiManifest?: any;
|
|
100
|
+
apiBaseUrl?: string;
|
|
101
|
+
apiPlayground?: boolean;
|
|
102
|
+
apiAuth?: {
|
|
103
|
+
type: "bearer" | "apiKey";
|
|
104
|
+
header?: string;
|
|
105
|
+
};
|
|
106
|
+
ApiReferenceComponent?: React.ComponentType<{
|
|
107
|
+
manifest: any;
|
|
108
|
+
baseUrl?: string;
|
|
109
|
+
showPlayground?: boolean;
|
|
110
|
+
playgroundAuth?: {
|
|
111
|
+
type: "bearer" | "apiKey";
|
|
112
|
+
header?: string;
|
|
113
|
+
};
|
|
114
|
+
}>;
|
|
88
115
|
onNavigate: (id: string) => void;
|
|
89
116
|
allPages: Array<{
|
|
90
117
|
id: string;
|
|
@@ -101,8 +128,17 @@ interface ShellProps {
|
|
|
101
128
|
content: string;
|
|
102
129
|
}>;
|
|
103
130
|
basePath?: string;
|
|
131
|
+
isDraft?: boolean;
|
|
132
|
+
dir?: "ltr" | "rtl";
|
|
133
|
+
overrides?: {
|
|
134
|
+
Header?: React.ComponentType<any>;
|
|
135
|
+
Footer?: React.ComponentType<any>;
|
|
136
|
+
Sidebar?: React.ComponentType<any>;
|
|
137
|
+
Toc?: React.ComponentType<any>;
|
|
138
|
+
PageFooter?: React.ComponentType<any>;
|
|
139
|
+
};
|
|
104
140
|
}
|
|
105
|
-
declare function Shell({ config, navigation, currentPageId, pageHtml, pageComponent, mdxComponents, pageTitle, pageDescription, headings, tocEnabled, editUrl, lastUpdated, changelogEntries, onNavigate, allPages, versioning, currentVersion, i18n, currentLocale, docContext, basePath, }: ShellProps): react_jsx_runtime.JSX.Element;
|
|
141
|
+
declare function Shell({ config, navigation, currentPageId, pageHtml, pageComponent, mdxComponents, pageTitle, pageDescription, headings, tocEnabled, editUrl, lastUpdated, changelogEntries, apiManifest, apiBaseUrl, apiPlayground, apiAuth, ApiReferenceComponent, onNavigate, allPages, versioning, currentVersion, i18n, currentLocale, docContext, basePath, isDraft, dir: dirProp, overrides, }: ShellProps): react_jsx_runtime.JSX.Element;
|
|
106
142
|
|
|
107
143
|
interface AiChatProps {
|
|
108
144
|
provider: "openai" | "anthropic" | "custom";
|
package/dist/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tomehq/theme",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.3",
|
|
4
4
|
"description": "Tome default theme and React app shell",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/index.tsx",
|
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
"./entry": "./src/entry.tsx"
|
|
10
10
|
},
|
|
11
11
|
"dependencies": {
|
|
12
|
-
"@tomehq/components": "0.3.
|
|
13
|
-
"@tomehq/core": "0.3.
|
|
12
|
+
"@tomehq/components": "0.3.3",
|
|
13
|
+
"@tomehq/core": "0.3.3"
|
|
14
14
|
},
|
|
15
15
|
"peerDependencies": {
|
|
16
16
|
"react": "^18.0.0 || ^19.0.0",
|
|
@@ -41,11 +41,11 @@
|
|
|
41
41
|
"license": "MIT",
|
|
42
42
|
"repository": {
|
|
43
43
|
"type": "git",
|
|
44
|
-
"url": "https://github.com/
|
|
44
|
+
"url": "https://github.com/tomehq/tome.git",
|
|
45
45
|
"directory": "packages/theme"
|
|
46
46
|
},
|
|
47
47
|
"scripts": {
|
|
48
|
-
"build": "tsup src/index.tsx src/entry.tsx --format esm --dts --external react --external react-dom --external 'virtual:tome/config' --external 'virtual:tome/routes' --external 'virtual:tome/page-loader' --external 'virtual:tome/doc-context' --external '@tomehq/components'",
|
|
48
|
+
"build": "tsup src/index.tsx src/entry.tsx --format esm --dts --external react --external react-dom --external 'virtual:tome/config' --external 'virtual:tome/routes' --external 'virtual:tome/page-loader' --external 'virtual:tome/doc-context' --external 'virtual:tome/overrides' --external '@tomehq/components'",
|
|
49
49
|
"dev": "tsup src/index.tsx src/entry.tsx --format esm --dts --external react --external react-dom --external 'virtual:tome/config' --external 'virtual:tome/routes' --external 'virtual:tome/page-loader' --external 'virtual:tome/doc-context' --external '@tomehq/components' --watch",
|
|
50
50
|
"clean": "rm -rf dist"
|
|
51
51
|
}
|
package/src/Shell.test.tsx
CHANGED
|
@@ -860,6 +860,115 @@ describe("Shell feedback widget", () => {
|
|
|
860
860
|
});
|
|
861
861
|
});
|
|
862
862
|
|
|
863
|
+
// ── Breadcrumbs ──────────────────────────────────────────
|
|
864
|
+
|
|
865
|
+
describe("Shell breadcrumbs", () => {
|
|
866
|
+
const multiSectionNav = [
|
|
867
|
+
{
|
|
868
|
+
section: "Getting Started",
|
|
869
|
+
pages: [
|
|
870
|
+
{ id: "intro", title: "Introduction", urlPath: "/intro" },
|
|
871
|
+
{ id: "quickstart", title: "Quick Start", urlPath: "/quickstart" },
|
|
872
|
+
],
|
|
873
|
+
},
|
|
874
|
+
{
|
|
875
|
+
section: "Guides",
|
|
876
|
+
pages: [
|
|
877
|
+
{ id: "search", title: "Search", urlPath: "/search" },
|
|
878
|
+
{ id: "deploy", title: "Deploy", urlPath: "/deploy" },
|
|
879
|
+
],
|
|
880
|
+
},
|
|
881
|
+
];
|
|
882
|
+
|
|
883
|
+
const multiSectionAllPages = [
|
|
884
|
+
{ id: "intro", title: "Introduction" },
|
|
885
|
+
{ id: "quickstart", title: "Quick Start" },
|
|
886
|
+
{ id: "search", title: "Search" },
|
|
887
|
+
{ id: "deploy", title: "Deploy" },
|
|
888
|
+
];
|
|
889
|
+
|
|
890
|
+
it("renders breadcrumbs for a non-index page", () => {
|
|
891
|
+
renderShell({
|
|
892
|
+
navigation: multiSectionNav,
|
|
893
|
+
allPages: multiSectionAllPages,
|
|
894
|
+
currentPageId: "quickstart",
|
|
895
|
+
pageTitle: "Quick Start",
|
|
896
|
+
});
|
|
897
|
+
const breadcrumbs = screen.getByTestId("breadcrumbs");
|
|
898
|
+
expect(breadcrumbs).toBeInTheDocument();
|
|
899
|
+
expect(within(breadcrumbs).getByText("Getting Started")).toBeInTheDocument();
|
|
900
|
+
expect(within(breadcrumbs).getByText("Quick Start")).toBeInTheDocument();
|
|
901
|
+
});
|
|
902
|
+
|
|
903
|
+
it("does not render breadcrumbs on the index page", () => {
|
|
904
|
+
renderShell({
|
|
905
|
+
currentPageId: "index",
|
|
906
|
+
pageTitle: "Home",
|
|
907
|
+
});
|
|
908
|
+
expect(screen.queryByTestId("breadcrumbs")).not.toBeInTheDocument();
|
|
909
|
+
});
|
|
910
|
+
|
|
911
|
+
it("renders section name as a link", () => {
|
|
912
|
+
renderShell({
|
|
913
|
+
navigation: multiSectionNav,
|
|
914
|
+
allPages: multiSectionAllPages,
|
|
915
|
+
currentPageId: "deploy",
|
|
916
|
+
pageTitle: "Deploy",
|
|
917
|
+
});
|
|
918
|
+
const breadcrumbs = screen.getByTestId("breadcrumbs");
|
|
919
|
+
const sectionLink = within(breadcrumbs).getByText("Guides");
|
|
920
|
+
expect(sectionLink.tagName).toBe("A");
|
|
921
|
+
});
|
|
922
|
+
|
|
923
|
+
it("renders current page as plain text (not a link)", () => {
|
|
924
|
+
renderShell({
|
|
925
|
+
navigation: multiSectionNav,
|
|
926
|
+
allPages: multiSectionAllPages,
|
|
927
|
+
currentPageId: "deploy",
|
|
928
|
+
pageTitle: "Deploy",
|
|
929
|
+
});
|
|
930
|
+
const breadcrumbs = screen.getByTestId("breadcrumbs");
|
|
931
|
+
const currentPage = within(breadcrumbs).getByText("Deploy");
|
|
932
|
+
expect(currentPage.tagName).toBe("SPAN");
|
|
933
|
+
});
|
|
934
|
+
|
|
935
|
+
it("clicking section link calls onNavigate with first page id", () => {
|
|
936
|
+
const onNavigate = vi.fn();
|
|
937
|
+
renderShell({
|
|
938
|
+
navigation: multiSectionNav,
|
|
939
|
+
allPages: multiSectionAllPages,
|
|
940
|
+
currentPageId: "deploy",
|
|
941
|
+
pageTitle: "Deploy",
|
|
942
|
+
onNavigate,
|
|
943
|
+
});
|
|
944
|
+
const breadcrumbs = screen.getByTestId("breadcrumbs");
|
|
945
|
+
fireEvent.click(within(breadcrumbs).getByText("Guides"));
|
|
946
|
+
expect(onNavigate).toHaveBeenCalledWith("search");
|
|
947
|
+
});
|
|
948
|
+
|
|
949
|
+
it("renders separator between breadcrumb items", () => {
|
|
950
|
+
renderShell({
|
|
951
|
+
navigation: multiSectionNav,
|
|
952
|
+
allPages: multiSectionAllPages,
|
|
953
|
+
currentPageId: "quickstart",
|
|
954
|
+
pageTitle: "Quick Start",
|
|
955
|
+
});
|
|
956
|
+
const breadcrumbs = screen.getByTestId("breadcrumbs");
|
|
957
|
+
expect(breadcrumbs.textContent).toContain("\u203A");
|
|
958
|
+
});
|
|
959
|
+
|
|
960
|
+
it("has proper aria-label for accessibility", () => {
|
|
961
|
+
renderShell({
|
|
962
|
+
navigation: multiSectionNav,
|
|
963
|
+
allPages: multiSectionAllPages,
|
|
964
|
+
currentPageId: "quickstart",
|
|
965
|
+
pageTitle: "Quick Start",
|
|
966
|
+
});
|
|
967
|
+
const nav = screen.getByRole("navigation", { name: "Breadcrumbs" });
|
|
968
|
+
expect(nav).toBeInTheDocument();
|
|
969
|
+
});
|
|
970
|
+
});
|
|
971
|
+
|
|
863
972
|
// ── Image zoom ───────────────────────────────────────────
|
|
864
973
|
|
|
865
974
|
describe("Shell image zoom", () => {
|
|
@@ -869,3 +978,299 @@ describe("Shell image zoom", () => {
|
|
|
869
978
|
expect(zoomOverlay).toBeNull();
|
|
870
979
|
});
|
|
871
980
|
});
|
|
981
|
+
|
|
982
|
+
// ── RTL support ──────────────────────────────────────────
|
|
983
|
+
|
|
984
|
+
describe("Shell RTL support", () => {
|
|
985
|
+
it("sets dir='rtl' on root element when dir prop is 'rtl'", () => {
|
|
986
|
+
const { container } = renderShell({ dir: "rtl" });
|
|
987
|
+
const root = container.querySelector("[dir='rtl']");
|
|
988
|
+
expect(root).not.toBeNull();
|
|
989
|
+
});
|
|
990
|
+
|
|
991
|
+
it("sets dir='ltr' on root element by default", () => {
|
|
992
|
+
const { container } = renderShell();
|
|
993
|
+
const root = container.querySelector("[dir='ltr']");
|
|
994
|
+
expect(root).not.toBeNull();
|
|
995
|
+
});
|
|
996
|
+
|
|
997
|
+
it("sets dir='ltr' when dir prop is explicitly 'ltr'", () => {
|
|
998
|
+
const { container } = renderShell({ dir: "ltr" });
|
|
999
|
+
const root = container.querySelector("[dir='ltr']");
|
|
1000
|
+
expect(root).not.toBeNull();
|
|
1001
|
+
expect(container.querySelector("[dir='rtl']")).toBeNull();
|
|
1002
|
+
});
|
|
1003
|
+
|
|
1004
|
+
it("resolves dir from i18n.localeDirs when dir prop is not set", () => {
|
|
1005
|
+
const { container } = renderShell({
|
|
1006
|
+
i18n: {
|
|
1007
|
+
defaultLocale: "en",
|
|
1008
|
+
locales: ["en", "ar"],
|
|
1009
|
+
localeDirs: { ar: "rtl" },
|
|
1010
|
+
},
|
|
1011
|
+
currentLocale: "ar",
|
|
1012
|
+
});
|
|
1013
|
+
const root = container.querySelector("[dir='rtl']");
|
|
1014
|
+
expect(root).not.toBeNull();
|
|
1015
|
+
});
|
|
1016
|
+
|
|
1017
|
+
it("defaults to ltr when locale has no entry in localeDirs", () => {
|
|
1018
|
+
const { container } = renderShell({
|
|
1019
|
+
i18n: {
|
|
1020
|
+
defaultLocale: "en",
|
|
1021
|
+
locales: ["en", "ar"],
|
|
1022
|
+
localeDirs: { ar: "rtl" },
|
|
1023
|
+
},
|
|
1024
|
+
currentLocale: "en",
|
|
1025
|
+
});
|
|
1026
|
+
const root = container.querySelector("[dir='ltr']");
|
|
1027
|
+
expect(root).not.toBeNull();
|
|
1028
|
+
});
|
|
1029
|
+
|
|
1030
|
+
it("reverses main layout flex direction for RTL", () => {
|
|
1031
|
+
const { container } = renderShell({ dir: "rtl" });
|
|
1032
|
+
const flexRow = container.querySelector('[style*="flex-direction: row-reverse"]');
|
|
1033
|
+
expect(flexRow).not.toBeNull();
|
|
1034
|
+
});
|
|
1035
|
+
|
|
1036
|
+
it("uses normal row direction for LTR", () => {
|
|
1037
|
+
const { container } = renderShell({ dir: "ltr" });
|
|
1038
|
+
const flexRowReverse = container.querySelector('[style*="flex-direction: row-reverse"]');
|
|
1039
|
+
expect(flexRowReverse).toBeNull();
|
|
1040
|
+
});
|
|
1041
|
+
|
|
1042
|
+
it("mirrors sidebar border to borderLeft for RTL", () => {
|
|
1043
|
+
const { container } = renderShell({ dir: "rtl" });
|
|
1044
|
+
const aside = container.querySelector("aside");
|
|
1045
|
+
expect(aside).not.toBeNull();
|
|
1046
|
+
expect(aside!.style.borderLeft).toContain("1px solid");
|
|
1047
|
+
});
|
|
1048
|
+
|
|
1049
|
+
it("renders without errors in RTL mode", () => {
|
|
1050
|
+
expect(() =>
|
|
1051
|
+
renderShell({ dir: "rtl" })
|
|
1052
|
+
).not.toThrow();
|
|
1053
|
+
});
|
|
1054
|
+
});
|
|
1055
|
+
|
|
1056
|
+
// ── API Reference (TOM-19) ──────────────────────────────
|
|
1057
|
+
|
|
1058
|
+
describe("Shell API reference rendering", () => {
|
|
1059
|
+
const mockManifest = {
|
|
1060
|
+
title: "Test API",
|
|
1061
|
+
version: "1.0.0",
|
|
1062
|
+
servers: [{ url: "https://api.example.com" }],
|
|
1063
|
+
tags: [{ name: "Users", description: "User management" }],
|
|
1064
|
+
endpoints: [
|
|
1065
|
+
{
|
|
1066
|
+
method: "GET",
|
|
1067
|
+
path: "/users",
|
|
1068
|
+
summary: "List all users",
|
|
1069
|
+
tags: ["Users"],
|
|
1070
|
+
parameters: [],
|
|
1071
|
+
responses: [{ status: "200", description: "OK" }],
|
|
1072
|
+
},
|
|
1073
|
+
],
|
|
1074
|
+
};
|
|
1075
|
+
|
|
1076
|
+
function MockApiRef({ manifest, baseUrl, showPlayground, playgroundAuth }: { manifest: any; baseUrl?: string; showPlayground?: boolean; playgroundAuth?: { type: string; header?: string } }) {
|
|
1077
|
+
return (
|
|
1078
|
+
<div data-testid="api-reference">
|
|
1079
|
+
<span data-testid="api-title">{manifest.title}</span>
|
|
1080
|
+
{baseUrl && <span data-testid="api-base-url">{baseUrl}</span>}
|
|
1081
|
+
{showPlayground && <span data-testid="api-playground">playground-enabled</span>}
|
|
1082
|
+
{playgroundAuth && <span data-testid="api-auth">{playgroundAuth.type}</span>}
|
|
1083
|
+
</div>
|
|
1084
|
+
);
|
|
1085
|
+
}
|
|
1086
|
+
|
|
1087
|
+
it("renders ApiReferenceComponent when both apiManifest and component are provided", () => {
|
|
1088
|
+
renderShell({
|
|
1089
|
+
apiManifest: mockManifest,
|
|
1090
|
+
ApiReferenceComponent: MockApiRef,
|
|
1091
|
+
pageHtml: undefined,
|
|
1092
|
+
});
|
|
1093
|
+
expect(screen.getByTestId("api-reference")).toBeInTheDocument();
|
|
1094
|
+
expect(screen.getByTestId("api-title")).toHaveTextContent("Test API");
|
|
1095
|
+
});
|
|
1096
|
+
|
|
1097
|
+
it("passes baseUrl to ApiReferenceComponent", () => {
|
|
1098
|
+
renderShell({
|
|
1099
|
+
apiManifest: mockManifest,
|
|
1100
|
+
ApiReferenceComponent: MockApiRef,
|
|
1101
|
+
apiBaseUrl: "https://api.example.com",
|
|
1102
|
+
pageHtml: undefined,
|
|
1103
|
+
});
|
|
1104
|
+
expect(screen.getByTestId("api-base-url")).toHaveTextContent("https://api.example.com");
|
|
1105
|
+
});
|
|
1106
|
+
|
|
1107
|
+
it("does not render API reference when only apiManifest is provided (no component)", () => {
|
|
1108
|
+
renderShell({
|
|
1109
|
+
apiManifest: mockManifest,
|
|
1110
|
+
pageHtml: "<p>Fallback content</p>",
|
|
1111
|
+
});
|
|
1112
|
+
expect(screen.queryByTestId("api-reference")).not.toBeInTheDocument();
|
|
1113
|
+
});
|
|
1114
|
+
|
|
1115
|
+
it("does not render API reference when only component is provided (no manifest)", () => {
|
|
1116
|
+
renderShell({
|
|
1117
|
+
ApiReferenceComponent: MockApiRef,
|
|
1118
|
+
pageHtml: "<p>Fallback content</p>",
|
|
1119
|
+
});
|
|
1120
|
+
expect(screen.queryByTestId("api-reference")).not.toBeInTheDocument();
|
|
1121
|
+
});
|
|
1122
|
+
|
|
1123
|
+
it("falls back to pageHtml when no apiManifest", () => {
|
|
1124
|
+
const { container } = renderShell({
|
|
1125
|
+
pageHtml: "<p>Regular page content</p>",
|
|
1126
|
+
});
|
|
1127
|
+
expect(container.querySelector(".tome-content")).toBeInTheDocument();
|
|
1128
|
+
expect(screen.queryByTestId("api-reference")).not.toBeInTheDocument();
|
|
1129
|
+
});
|
|
1130
|
+
|
|
1131
|
+
it("API reference takes precedence over changelog entries", () => {
|
|
1132
|
+
const changelogEntries = [
|
|
1133
|
+
{ version: "1.0.0", sections: [{ type: "Added", items: ["Feature"] }] },
|
|
1134
|
+
];
|
|
1135
|
+
renderShell({
|
|
1136
|
+
apiManifest: mockManifest,
|
|
1137
|
+
ApiReferenceComponent: MockApiRef,
|
|
1138
|
+
changelogEntries,
|
|
1139
|
+
pageHtml: undefined,
|
|
1140
|
+
});
|
|
1141
|
+
// API reference should render, not changelog
|
|
1142
|
+
expect(screen.getByTestId("api-reference")).toBeInTheDocument();
|
|
1143
|
+
expect(screen.queryByTestId("changelog-timeline")).not.toBeInTheDocument();
|
|
1144
|
+
});
|
|
1145
|
+
|
|
1146
|
+
it("API reference takes precedence over pageComponent", () => {
|
|
1147
|
+
const PageComp = () => <div data-testid="page-comp">MDX content</div>;
|
|
1148
|
+
renderShell({
|
|
1149
|
+
apiManifest: mockManifest,
|
|
1150
|
+
ApiReferenceComponent: MockApiRef,
|
|
1151
|
+
pageComponent: PageComp,
|
|
1152
|
+
pageHtml: undefined,
|
|
1153
|
+
});
|
|
1154
|
+
expect(screen.getByTestId("api-reference")).toBeInTheDocument();
|
|
1155
|
+
expect(screen.queryByTestId("page-comp")).not.toBeInTheDocument();
|
|
1156
|
+
});
|
|
1157
|
+
|
|
1158
|
+
it("passes showPlayground prop to ApiReferenceComponent", () => {
|
|
1159
|
+
renderShell({
|
|
1160
|
+
apiManifest: mockManifest,
|
|
1161
|
+
ApiReferenceComponent: MockApiRef,
|
|
1162
|
+
apiPlayground: true,
|
|
1163
|
+
pageHtml: undefined,
|
|
1164
|
+
});
|
|
1165
|
+
expect(screen.getByTestId("api-playground")).toHaveTextContent("playground-enabled");
|
|
1166
|
+
});
|
|
1167
|
+
|
|
1168
|
+
it("passes playgroundAuth prop to ApiReferenceComponent", () => {
|
|
1169
|
+
renderShell({
|
|
1170
|
+
apiManifest: mockManifest,
|
|
1171
|
+
ApiReferenceComponent: MockApiRef,
|
|
1172
|
+
apiPlayground: true,
|
|
1173
|
+
apiAuth: { type: "bearer" },
|
|
1174
|
+
pageHtml: undefined,
|
|
1175
|
+
});
|
|
1176
|
+
expect(screen.getByTestId("api-auth")).toHaveTextContent("bearer");
|
|
1177
|
+
});
|
|
1178
|
+
|
|
1179
|
+
it("does not pass playground props when not provided", () => {
|
|
1180
|
+
renderShell({
|
|
1181
|
+
apiManifest: mockManifest,
|
|
1182
|
+
ApiReferenceComponent: MockApiRef,
|
|
1183
|
+
pageHtml: undefined,
|
|
1184
|
+
});
|
|
1185
|
+
expect(screen.getByTestId("api-reference")).toBeInTheDocument();
|
|
1186
|
+
expect(screen.queryByTestId("api-playground")).not.toBeInTheDocument();
|
|
1187
|
+
expect(screen.queryByTestId("api-auth")).not.toBeInTheDocument();
|
|
1188
|
+
});
|
|
1189
|
+
});
|
|
1190
|
+
|
|
1191
|
+
// ── Content link interception ───────────────────────────────
|
|
1192
|
+
|
|
1193
|
+
describe("Content link interception", () => {
|
|
1194
|
+
it("intercepts internal bare page ID links and calls onNavigate", () => {
|
|
1195
|
+
const onNavigate = vi.fn();
|
|
1196
|
+
renderShell({
|
|
1197
|
+
onNavigate,
|
|
1198
|
+
pageHtml: '<p><a href="quickstart">Go to quickstart</a></p>',
|
|
1199
|
+
});
|
|
1200
|
+
const link = screen.getByText("Go to quickstart");
|
|
1201
|
+
fireEvent.click(link);
|
|
1202
|
+
expect(onNavigate).toHaveBeenCalledWith("quickstart");
|
|
1203
|
+
});
|
|
1204
|
+
|
|
1205
|
+
it("intercepts ./relative links and strips the prefix", () => {
|
|
1206
|
+
const onNavigate = vi.fn();
|
|
1207
|
+
renderShell({
|
|
1208
|
+
onNavigate,
|
|
1209
|
+
pageHtml: '<p><a href="./installation">Install</a></p>',
|
|
1210
|
+
});
|
|
1211
|
+
fireEvent.click(screen.getByText("Install"));
|
|
1212
|
+
expect(onNavigate).toHaveBeenCalledWith("installation");
|
|
1213
|
+
});
|
|
1214
|
+
|
|
1215
|
+
it("does not intercept external http links", () => {
|
|
1216
|
+
const onNavigate = vi.fn();
|
|
1217
|
+
renderShell({
|
|
1218
|
+
onNavigate,
|
|
1219
|
+
pageHtml: '<p><a href="https://example.com">External</a></p>',
|
|
1220
|
+
});
|
|
1221
|
+
fireEvent.click(screen.getByText("External"));
|
|
1222
|
+
expect(onNavigate).not.toHaveBeenCalled();
|
|
1223
|
+
});
|
|
1224
|
+
|
|
1225
|
+
it("does not intercept mailto links", () => {
|
|
1226
|
+
const onNavigate = vi.fn();
|
|
1227
|
+
renderShell({
|
|
1228
|
+
onNavigate,
|
|
1229
|
+
pageHtml: '<p><a href="mailto:test@example.com">Email</a></p>',
|
|
1230
|
+
});
|
|
1231
|
+
fireEvent.click(screen.getByText("Email"));
|
|
1232
|
+
expect(onNavigate).not.toHaveBeenCalled();
|
|
1233
|
+
});
|
|
1234
|
+
|
|
1235
|
+
it("does not intercept pure anchor links", () => {
|
|
1236
|
+
const onNavigate = vi.fn();
|
|
1237
|
+
renderShell({
|
|
1238
|
+
onNavigate,
|
|
1239
|
+
pageHtml: '<p><a href="#section-1">Jump to section</a></p>',
|
|
1240
|
+
});
|
|
1241
|
+
fireEvent.click(screen.getByText("Jump to section"));
|
|
1242
|
+
expect(onNavigate).not.toHaveBeenCalled();
|
|
1243
|
+
});
|
|
1244
|
+
|
|
1245
|
+
it("strips basePath prefix from links", () => {
|
|
1246
|
+
const onNavigate = vi.fn();
|
|
1247
|
+
renderShell({
|
|
1248
|
+
onNavigate,
|
|
1249
|
+
basePath: "/docs/",
|
|
1250
|
+
pageHtml: '<p><a href="/docs/quickstart">Go</a></p>',
|
|
1251
|
+
});
|
|
1252
|
+
fireEvent.click(screen.getByText("Go"));
|
|
1253
|
+
expect(onNavigate).toHaveBeenCalledWith("quickstart");
|
|
1254
|
+
});
|
|
1255
|
+
|
|
1256
|
+
it("resolves basePath-only link to index", () => {
|
|
1257
|
+
const onNavigate = vi.fn();
|
|
1258
|
+
renderShell({
|
|
1259
|
+
onNavigate,
|
|
1260
|
+
basePath: "/docs/",
|
|
1261
|
+
pageHtml: '<p><a href="/docs/">Home</a></p>',
|
|
1262
|
+
});
|
|
1263
|
+
fireEvent.click(screen.getByText("Home"));
|
|
1264
|
+
expect(onNavigate).toHaveBeenCalledWith("index");
|
|
1265
|
+
});
|
|
1266
|
+
|
|
1267
|
+
it("resolves empty path to index", () => {
|
|
1268
|
+
const onNavigate = vi.fn();
|
|
1269
|
+
renderShell({
|
|
1270
|
+
onNavigate,
|
|
1271
|
+
pageHtml: '<p><a href="./">Home</a></p>',
|
|
1272
|
+
});
|
|
1273
|
+
fireEvent.click(screen.getByText("Home"));
|
|
1274
|
+
expect(onNavigate).toHaveBeenCalledWith("index");
|
|
1275
|
+
});
|
|
1276
|
+
});
|