@backstage/plugin-techdocs 1.7.1-next.1 → 1.8.0

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.
@@ -1,22 +1,19 @@
1
- import { createApiRef, useApi, configApiRef, useAnalytics, createRouteRef, useRouteRef, useRouteRefParams, getComponentData, createPlugin, createApiFactory, discoveryApiRef, identityApiRef, fetchApiRef, createRoutableExtension } from '@backstage/core-plugin-api';
2
- import { ResponseError, NotFoundError } from '@backstage/errors';
3
- import { EventSourcePolyfill } from 'event-source-polyfill';
4
1
  import React, { useReducer, useRef, useMemo, createContext, useContext, useState, useEffect, useCallback, Children } from 'react';
5
- import { useParams, useNavigate, useOutlet, Routes, Route, useRoutes } from 'react-router-dom';
6
- import { techdocsStorageApiRef as techdocsStorageApiRef$1, useTechDocsReaderPage, SHADOW_DOM_STYLE_LOAD_EVENT, useShadowDomStylesLoading, useTechDocsAddons, TechDocsAddonLocations, TechDocsShadowDom, TECHDOCS_ADDONS_WRAPPER_KEY, TECHDOCS_ADDONS_KEY, TechDocsReaderPageProvider, techdocsApiRef as techdocsApiRef$1 } from '@backstage/plugin-techdocs-react';
7
- import useAsync from 'react-use/lib/useAsync';
8
- import useAsyncRetry from 'react-use/lib/useAsyncRetry';
9
- import { LogViewer, ErrorPage, useSidebarPinState, Content, HeaderLabel, Header, Page, ItemCardGrid, ItemCardHeader, LinkButton, WarningPanel, CodeSnippet, Progress, Link, ContentHeader, SubvalueCell, Table, EmptyState, PageWithHeader, SupportButton, MissingAnnotationEmptyState } from '@backstage/core-components';
10
- import { makeStyles, createStyles, Button, Drawer, Grid, Typography, IconButton, CircularProgress, lighten, alpha, useTheme, withStyles, Tooltip, ThemeProvider, SvgIcon, useMediaQuery, Portal, Toolbar, Box, Menu, Card, CardMedia, CardContent, CardActions } from '@material-ui/core';
11
- import { SearchContextProvider, useSearch, SearchAutocomplete, createSearchResultListItemExtension } from '@backstage/plugin-search-react';
12
- import { TechDocsSearchResultListItem as TechDocsSearchResultListItem$1 } from './TechDocsSearchResultListItem-4736f829.esm.js';
2
+ import { useParams, useNavigate, useOutlet } from 'react-router-dom';
3
+ import { LogViewer, ErrorPage, useSidebarPinState, Content, HeaderLabel, Header, Page } from '@backstage/core-components';
4
+ import { techdocsStorageApiRef, useTechDocsReaderPage, SHADOW_DOM_STYLE_LOAD_EVENT, useShadowDomStylesLoading, useTechDocsAddons, TechDocsAddonLocations, TechDocsShadowDom, TECHDOCS_ADDONS_WRAPPER_KEY, TECHDOCS_ADDONS_KEY, TechDocsReaderPageProvider } from '@backstage/plugin-techdocs-react';
5
+ import { makeStyles, createStyles, Button, Drawer, Grid, Typography, IconButton, CircularProgress, lighten, alpha, useTheme, withStyles, Tooltip, ThemeProvider, SvgIcon, useMediaQuery, Portal, Toolbar, Box, Menu } from '@material-ui/core';
6
+ import { SearchContextProvider, useSearch, SearchAutocomplete } from '@backstage/plugin-search-react';
7
+ import { TechDocsSearchResultListItem } from './TechDocsSearchResultListItem-4736f829.esm.js';
13
8
  import { Alert, Skeleton } from '@material-ui/lab';
14
9
  import Close from '@material-ui/icons/Close';
10
+ import { useApi, configApiRef, useAnalytics, useRouteRef, useRouteRefParams, getComponentData } from '@backstage/core-plugin-api';
11
+ import useAsync from 'react-use/lib/useAsync';
12
+ import useAsyncRetry from 'react-use/lib/useAsyncRetry';
15
13
  import { scmIntegrationsApiRef } from '@backstage/integration-react';
16
14
  import DOMPurify from 'dompurify';
17
15
  import { replaceGithubUrlType } from '@backstage/integration';
18
16
  import FeedbackOutlinedIcon from '@material-ui/icons/FeedbackOutlined';
19
- import ReactDOM from 'react-dom';
20
17
  import parseGitUrl from 'git-url-parse';
21
18
  import MenuIcon from '@material-ui/icons/Menu';
22
19
  import IconButton$1 from '@material-ui/core/IconButton';
@@ -24,182 +21,11 @@ import useCopyToClipboard from 'react-use/lib/useCopyToClipboard';
24
21
  import Helmet from 'react-helmet';
25
22
  import { useTheme as useTheme$1 } from '@material-ui/core/styles';
26
23
  import CodeIcon from '@material-ui/icons/Code';
27
- import { getEntityRelations, EntityRefLink, EntityRefLinks, useEntityList, useEntityOwnership, humanizeEntityRef, useStarredEntities, CATALOG_FILTER_EXISTS, EntityListProvider, CatalogFilterLayout, UserListPicker, EntityOwnerPicker, EntityTagPicker, useEntity } from '@backstage/plugin-catalog-react';
28
- import { RELATION_OWNED_BY, getCompoundEntityRef, parseEntityRef } from '@backstage/catalog-model';
24
+ import { getEntityRelations, EntityRefLink, EntityRefLinks } from '@backstage/plugin-catalog-react';
25
+ import { RELATION_OWNED_BY } from '@backstage/catalog-model';
29
26
  import { capitalize } from 'lodash';
27
+ import { a as rootRouteRef, r as rootDocsRouteRef } from './routes-f8adf6c9.esm.js';
30
28
  import SettingsIcon from '@material-ui/icons/Settings';
31
- import ShareIcon from '@material-ui/icons/Share';
32
- import { withStyles as withStyles$1 } from '@material-ui/styles';
33
- import Star from '@material-ui/icons/Star';
34
- import StarBorder from '@material-ui/icons/StarBorder';
35
-
36
- const techdocsStorageApiRef = createApiRef({
37
- id: "plugin.techdocs.storageservice"
38
- });
39
- const techdocsApiRef = createApiRef({
40
- id: "plugin.techdocs.service"
41
- });
42
-
43
- var __defProp = Object.defineProperty;
44
- var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
45
- var __publicField = (obj, key, value) => {
46
- __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
47
- return value;
48
- };
49
- class TechDocsClient {
50
- constructor(options) {
51
- __publicField(this, "configApi");
52
- __publicField(this, "discoveryApi");
53
- __publicField(this, "fetchApi");
54
- this.configApi = options.configApi;
55
- this.discoveryApi = options.discoveryApi;
56
- this.fetchApi = options.fetchApi;
57
- }
58
- async getApiOrigin() {
59
- return await this.discoveryApi.getBaseUrl("techdocs");
60
- }
61
- /**
62
- * Retrieve TechDocs metadata.
63
- *
64
- * When docs are built, we generate a techdocs_metadata.json and store it along with the generated
65
- * static files. It includes necessary data about the docs site. This method requests techdocs-backend
66
- * which retrieves the TechDocs metadata.
67
- *
68
- * @param entityId - Object containing entity data like name, namespace, etc.
69
- */
70
- async getTechDocsMetadata(entityId) {
71
- const { kind, namespace, name } = entityId;
72
- const apiOrigin = await this.getApiOrigin();
73
- const requestUrl = `${apiOrigin}/metadata/techdocs/${namespace}/${kind}/${name}`;
74
- const request = await this.fetchApi.fetch(`${requestUrl}`);
75
- if (!request.ok) {
76
- throw await ResponseError.fromResponse(request);
77
- }
78
- return await request.json();
79
- }
80
- /**
81
- * Retrieve metadata about an entity.
82
- *
83
- * This method requests techdocs-backend which uses the catalog APIs to respond with filtered
84
- * information required here.
85
- *
86
- * @param entityId - Object containing entity data like name, namespace, etc.
87
- */
88
- async getEntityMetadata(entityId) {
89
- const { kind, namespace, name } = entityId;
90
- const apiOrigin = await this.getApiOrigin();
91
- const requestUrl = `${apiOrigin}/metadata/entity/${namespace}/${kind}/${name}`;
92
- const request = await this.fetchApi.fetch(`${requestUrl}`);
93
- if (!request.ok) {
94
- throw await ResponseError.fromResponse(request);
95
- }
96
- return await request.json();
97
- }
98
- }
99
- class TechDocsStorageClient {
100
- constructor(options) {
101
- __publicField(this, "configApi");
102
- __publicField(this, "discoveryApi");
103
- __publicField(this, "identityApi");
104
- __publicField(this, "fetchApi");
105
- this.configApi = options.configApi;
106
- this.discoveryApi = options.discoveryApi;
107
- this.identityApi = options.identityApi;
108
- this.fetchApi = options.fetchApi;
109
- }
110
- async getApiOrigin() {
111
- return await this.discoveryApi.getBaseUrl("techdocs");
112
- }
113
- async getStorageUrl() {
114
- var _a;
115
- return (_a = this.configApi.getOptionalString("techdocs.storageUrl")) != null ? _a : `${await this.discoveryApi.getBaseUrl("techdocs")}/static/docs`;
116
- }
117
- async getBuilder() {
118
- return this.configApi.getString("techdocs.builder");
119
- }
120
- /**
121
- * Fetch HTML content as text for an individual docs page in an entity's docs site.
122
- *
123
- * @param entityId - Object containing entity data like name, namespace, etc.
124
- * @param path - The unique path to an individual docs page e.g. overview/what-is-new
125
- * @returns HTML content of the docs page as string
126
- * @throws Throws error when the page is not found.
127
- */
128
- async getEntityDocs(entityId, path) {
129
- const { kind, namespace, name } = entityId;
130
- const storageUrl = await this.getStorageUrl();
131
- const url = `${storageUrl}/${namespace}/${kind}/${name}/${path}`;
132
- const request = await this.fetchApi.fetch(
133
- `${url.endsWith("/") ? url : `${url}/`}index.html`
134
- );
135
- let errorMessage = "";
136
- switch (request.status) {
137
- case 404:
138
- errorMessage = "Page not found. ";
139
- if (!path) {
140
- errorMessage += "This could be because there is no index.md file in the root of the docs directory of this repository.";
141
- }
142
- throw new NotFoundError(errorMessage);
143
- case 500:
144
- errorMessage = "Could not generate documentation or an error in the TechDocs backend. ";
145
- throw new Error(errorMessage);
146
- }
147
- return request.text();
148
- }
149
- /**
150
- * Check if docs are on the latest version and trigger rebuild if not
151
- *
152
- * @param entityId - Object containing entity data like name, namespace, etc.
153
- * @param logHandler - Callback to receive log messages from the build process
154
- * @returns Whether documents are currently synchronized to newest version
155
- * @throws Throws error on error from sync endpoint in TechDocs Backend
156
- */
157
- async syncEntityDocs(entityId, logHandler = () => {
158
- }) {
159
- const { kind, namespace, name } = entityId;
160
- const apiOrigin = await this.getApiOrigin();
161
- const url = `${apiOrigin}/sync/${namespace}/${kind}/${name}`;
162
- const { token } = await this.identityApi.getCredentials();
163
- return new Promise((resolve, reject) => {
164
- const source = new EventSourcePolyfill(url, {
165
- withCredentials: true,
166
- headers: token ? { Authorization: `Bearer ${token}` } : {}
167
- });
168
- source.addEventListener("log", (e) => {
169
- if (e.data) {
170
- logHandler(JSON.parse(e.data));
171
- }
172
- });
173
- source.addEventListener("finish", (e) => {
174
- let updated = false;
175
- if (e.data) {
176
- ({ updated } = JSON.parse(e.data));
177
- }
178
- resolve(updated ? "updated" : "cached");
179
- });
180
- source.onerror = (e) => {
181
- source.close();
182
- switch (e.status) {
183
- case 404:
184
- reject(new NotFoundError(e.message));
185
- return;
186
- default:
187
- reject(new Error(e.data));
188
- return;
189
- }
190
- };
191
- });
192
- }
193
- async getBaseUrl(oldBaseUrl, entityId, path) {
194
- const { kind, namespace, name } = entityId;
195
- const apiOrigin = await this.getApiOrigin();
196
- const newBaseUrl = `${apiOrigin}/static/docs/${namespace}/${kind}/${name}/${path}`;
197
- return new URL(
198
- oldBaseUrl,
199
- newBaseUrl.endsWith("/") ? newBaseUrl : `${newBaseUrl}/`
200
- ).toString();
201
- }
202
- }
203
29
 
204
30
  function calculateDisplayState({
205
31
  contentLoading,
@@ -274,7 +100,7 @@ function useReaderState(kind, namespace, name, path) {
274
100
  contentLoading: true,
275
101
  buildLog: []
276
102
  });
277
- const techdocsStorageApi = useApi(techdocsStorageApiRef$1);
103
+ const techdocsStorageApi = useApi(techdocsStorageApiRef);
278
104
  const { retry: contentReload } = useAsyncRetry(async () => {
279
105
  dispatch({ type: "contentLoading" });
280
106
  try {
@@ -429,7 +255,7 @@ const TechDocsSearchBar = (props) => {
429
255
  value: null,
430
256
  options,
431
257
  renderOption: ({ document, highlight }) => /* @__PURE__ */ React.createElement(
432
- TechDocsSearchResultListItem$1,
258
+ TechDocsSearchResultListItem,
433
259
  {
434
260
  result: document,
435
261
  lineClamp: 3,
@@ -1312,6 +1138,20 @@ const addBaseUrl = ({
1312
1138
  };
1313
1139
  };
1314
1140
 
1141
+ let ReactDOM;
1142
+ if (process.env.HAS_REACT_DOM_CLIENT) {
1143
+ ReactDOM = require("react-dom/client");
1144
+ } else {
1145
+ ReactDOM = require("react-dom");
1146
+ }
1147
+ function renderReactElement(element, root) {
1148
+ if ("createRoot" in ReactDOM) {
1149
+ ReactDOM.createRoot(root).render(element);
1150
+ } else {
1151
+ ReactDOM.render(element, root);
1152
+ }
1153
+ }
1154
+
1315
1155
  const addGitFeedbackLink = (scmIntegrationsApi) => {
1316
1156
  return (dom) => {
1317
1157
  var _a;
@@ -1348,7 +1188,7 @@ Feedback:`
1348
1188
  default:
1349
1189
  return dom;
1350
1190
  }
1351
- ReactDOM.render(React.createElement(FeedbackOutlinedIcon), feedbackLink);
1191
+ renderReactElement(React.createElement(FeedbackOutlinedIcon), feedbackLink);
1352
1192
  feedbackLink.style.paddingLeft = "5px";
1353
1193
  feedbackLink.title = "Leave feedback for this page";
1354
1194
  feedbackLink.id = "git-feedback-link";
@@ -1367,7 +1207,7 @@ const addSidebarToggle = () => {
1367
1207
  return dom;
1368
1208
  }
1369
1209
  const toggleSidebar = mkdocsToggleSidebar.cloneNode();
1370
- ReactDOM.render(React.createElement(MenuIcon), toggleSidebar);
1210
+ renderReactElement(React.createElement(MenuIcon), toggleSidebar);
1371
1211
  toggleSidebar.id = "toggle-sidebar";
1372
1212
  toggleSidebar.title = "Toggle Sidebar";
1373
1213
  toggleSidebar.classList.add("md-content__button");
@@ -1483,7 +1323,7 @@ const copyToClipboard = (theme) => {
1483
1323
  const text = code.textContent || "";
1484
1324
  const container = document.createElement("div");
1485
1325
  (_a = code == null ? void 0 : code.parentElement) == null ? void 0 : _a.prepend(container);
1486
- ReactDOM.render(
1326
+ renderReactElement(
1487
1327
  /* @__PURE__ */ React.createElement(ThemeProvider, { theme }, /* @__PURE__ */ React.createElement(CopyToClipboardButton, { text })),
1488
1328
  container
1489
1329
  );
@@ -1621,7 +1461,7 @@ const useTechDocsReaderDom = (entityRef) => {
1621
1461
  const sanitizerTransformer = useSanitizerTransformer();
1622
1462
  const stylesTransformer = useStylesTransformer();
1623
1463
  const analytics = useAnalytics();
1624
- const techdocsStorageApi = useApi(techdocsStorageApiRef$1);
1464
+ const techdocsStorageApi = useApi(techdocsStorageApiRef);
1625
1465
  const scmIntegrationsApi = useApi(scmIntegrationsApiRef);
1626
1466
  const { state, path, content: rawPage } = useTechDocsReader();
1627
1467
  const [dom, setDom] = useState(null);
@@ -1865,17 +1705,6 @@ const TechDocsReaderPageContent = withTechDocsReaderProvider(
1865
1705
  );
1866
1706
  const Reader = TechDocsReaderPageContent;
1867
1707
 
1868
- const rootRouteRef = createRouteRef({
1869
- id: "techdocs:index-page"
1870
- });
1871
- const rootDocsRouteRef = createRouteRef({
1872
- id: "techdocs:reader-page",
1873
- params: ["namespace", "kind", "name"]
1874
- });
1875
- const rootCatalogDocsRouteRef = createRouteRef({
1876
- id: "techdocs:catalog-reader-view"
1877
- });
1878
-
1879
1708
  const skeleton = /* @__PURE__ */ React.createElement(Skeleton, { animation: "wave", variant: "text", height: 40 });
1880
1709
  const TechDocsReaderPageHeader = (props) => {
1881
1710
  const {
@@ -1940,7 +1769,7 @@ const TechDocsReaderPageHeader = (props) => {
1940
1769
  }
1941
1770
  )
1942
1771
  }
1943
- ), lifecycle ? /* @__PURE__ */ React.createElement(HeaderLabel, { label: "Lifecycle", value: lifecycle }) : null, locationMetadata && locationMetadata.type !== "dir" && locationMetadata.type !== "file" ? /* @__PURE__ */ React.createElement(
1772
+ ), lifecycle ? /* @__PURE__ */ React.createElement(HeaderLabel, { label: "Lifecycle", value: String(lifecycle) }) : null, locationMetadata && locationMetadata.type !== "dir" && locationMetadata.type !== "file" ? /* @__PURE__ */ React.createElement(
1944
1773
  HeaderLabel,
1945
1774
  {
1946
1775
  label: "",
@@ -2046,7 +1875,7 @@ const TechDocsReaderLayout = (props) => {
2046
1875
  const { withSearch, withHeader = true } = props;
2047
1876
  return /* @__PURE__ */ React.createElement(Page, { themeId: "documentation" }, withHeader && /* @__PURE__ */ React.createElement(TechDocsReaderPageHeader, null), /* @__PURE__ */ React.createElement(TechDocsReaderPageSubheader, null), /* @__PURE__ */ React.createElement(TechDocsReaderPageContent, { withSearch }));
2048
1877
  };
2049
- const TechDocsReaderPage$1 = (props) => {
1878
+ const TechDocsReaderPage = (props) => {
2050
1879
  const { kind, name, namespace } = useRouteRefParams(rootDocsRouteRef);
2051
1880
  const { children, entityRef = { kind, name, namespace } } = props;
2052
1881
  const outlet = useOutlet();
@@ -2071,485 +1900,5 @@ const TechDocsReaderPage$1 = (props) => {
2071
1900
  }) : children)));
2072
1901
  };
2073
1902
 
2074
- function toLowerMaybe(str, config) {
2075
- return config.getOptionalBoolean(
2076
- "techdocs.legacyUseCaseSensitiveTripletPaths"
2077
- ) ? str : str.toLocaleLowerCase("en-US");
2078
- }
2079
-
2080
- const DocsCardGrid = (props) => {
2081
- const { entities } = props;
2082
- const getRouteToReaderPageFor = useRouteRef(rootDocsRouteRef);
2083
- const config = useApi(configApiRef);
2084
- if (!entities)
2085
- return null;
2086
- return /* @__PURE__ */ React.createElement(ItemCardGrid, { "data-testid": "docs-explore" }, !(entities == null ? void 0 : entities.length) ? null : entities.map((entity, index) => {
2087
- var _a, _b;
2088
- return /* @__PURE__ */ React.createElement(Card, { key: index }, /* @__PURE__ */ React.createElement(CardMedia, null, /* @__PURE__ */ React.createElement(
2089
- ItemCardHeader,
2090
- {
2091
- title: (_a = entity.metadata.title) != null ? _a : entity.metadata.name
2092
- }
2093
- )), /* @__PURE__ */ React.createElement(CardContent, null, entity.metadata.description), /* @__PURE__ */ React.createElement(CardActions, null, /* @__PURE__ */ React.createElement(
2094
- LinkButton,
2095
- {
2096
- to: getRouteToReaderPageFor({
2097
- namespace: toLowerMaybe(
2098
- (_b = entity.metadata.namespace) != null ? _b : "default",
2099
- config
2100
- ),
2101
- kind: toLowerMaybe(entity.kind, config),
2102
- name: toLowerMaybe(entity.metadata.name, config)
2103
- }),
2104
- color: "primary",
2105
- "data-testid": "read_docs"
2106
- },
2107
- "Read Docs"
2108
- )));
2109
- }));
2110
- };
2111
-
2112
- const allEntitiesGroup = {
2113
- title: "All Documentation",
2114
- filterPredicate: () => true
2115
- };
2116
- const EntityListDocsGridGroup = (props) => {
2117
- const { entities, group } = props;
2118
- const { loading: loadingOwnership, isOwnedEntity } = useEntityOwnership();
2119
- const shownEntities = entities.filter((entity) => {
2120
- if (group.filterPredicate === "ownedByUser") {
2121
- if (loadingOwnership) {
2122
- return false;
2123
- }
2124
- return isOwnedEntity(entity);
2125
- }
2126
- return typeof group.filterPredicate === "function" && group.filterPredicate(entity);
2127
- });
2128
- const titleComponent = (() => {
2129
- return typeof group.title === "string" ? /* @__PURE__ */ React.createElement(ContentHeader, { title: group.title }) : group.title;
2130
- })();
2131
- if (shownEntities.length === 0) {
2132
- return null;
2133
- }
2134
- return /* @__PURE__ */ React.createElement(Content, null, titleComponent, /* @__PURE__ */ React.createElement(DocsCardGrid, { entities: shownEntities }));
2135
- };
2136
- const EntityListDocsGrid = (props) => {
2137
- const { loading, error, entities } = useEntityList();
2138
- if (error) {
2139
- return /* @__PURE__ */ React.createElement(
2140
- WarningPanel,
2141
- {
2142
- severity: "error",
2143
- title: "Could not load available documentation."
2144
- },
2145
- /* @__PURE__ */ React.createElement(CodeSnippet, { language: "text", text: error.toString() })
2146
- );
2147
- }
2148
- if (loading) {
2149
- return /* @__PURE__ */ React.createElement(Progress, null);
2150
- }
2151
- if (entities.length === 0) {
2152
- return /* @__PURE__ */ React.createElement("div", { "data-testid": "doc-not-found" }, /* @__PURE__ */ React.createElement(Typography, { variant: "body2" }, "No documentation found that match your filter. Learn more about", " ", /* @__PURE__ */ React.createElement(Link, { to: "https://backstage.io/docs/features/techdocs/creating-and-publishing" }, "publishing documentation"), "."));
2153
- }
2154
- entities.sort(
2155
- (a, b) => {
2156
- var _a, _b;
2157
- return ((_a = a.metadata.title) != null ? _a : a.metadata.name).localeCompare(
2158
- (_b = b.metadata.title) != null ? _b : b.metadata.name
2159
- );
2160
- }
2161
- );
2162
- return /* @__PURE__ */ React.createElement(Content, null, (props.groups || [allEntitiesGroup]).map((group, index) => /* @__PURE__ */ React.createElement(
2163
- EntityListDocsGridGroup,
2164
- {
2165
- entities,
2166
- group,
2167
- key: `${group.title}-${index}`
2168
- }
2169
- )));
2170
- };
2171
-
2172
- const YellowStar = withStyles$1({
2173
- root: {
2174
- color: "#f3ba37"
2175
- }
2176
- })(Star);
2177
- const actionFactories = {
2178
- createCopyDocsUrlAction(copyToClipboard) {
2179
- return (row) => {
2180
- return {
2181
- icon: () => /* @__PURE__ */ React.createElement(ShareIcon, { fontSize: "small" }),
2182
- tooltip: "Click to copy documentation link to clipboard",
2183
- onClick: () => copyToClipboard(`${window.location.origin}${row.resolved.docsUrl}`)
2184
- };
2185
- };
2186
- },
2187
- createStarEntityAction(isStarredEntity, toggleStarredEntity) {
2188
- return (row) => {
2189
- const entity = row.entity;
2190
- const isStarred = isStarredEntity(entity);
2191
- return {
2192
- cellStyle: { paddingLeft: "1em" },
2193
- icon: () => isStarred ? /* @__PURE__ */ React.createElement(YellowStar, null) : /* @__PURE__ */ React.createElement(StarBorder, null),
2194
- tooltip: isStarred ? "Remove from favorites" : "Add to favorites",
2195
- onClick: () => toggleStarredEntity(entity)
2196
- };
2197
- };
2198
- }
2199
- };
2200
-
2201
- function customTitle(entity) {
2202
- return entity.metadata.title || entity.metadata.name;
2203
- }
2204
- const columnFactories = {
2205
- createNameColumn() {
2206
- return {
2207
- title: "Document",
2208
- field: "entity.metadata.name",
2209
- highlight: true,
2210
- render: (row) => /* @__PURE__ */ React.createElement(
2211
- SubvalueCell,
2212
- {
2213
- value: /* @__PURE__ */ React.createElement(Link, { to: row.resolved.docsUrl }, customTitle(row.entity)),
2214
- subvalue: row.entity.metadata.description
2215
- }
2216
- )
2217
- };
2218
- },
2219
- createOwnerColumn() {
2220
- return {
2221
- title: "Owner",
2222
- field: "resolved.ownedByRelationsTitle",
2223
- render: ({ resolved }) => /* @__PURE__ */ React.createElement(
2224
- EntityRefLinks,
2225
- {
2226
- entityRefs: resolved.ownedByRelations,
2227
- defaultKind: "group"
2228
- }
2229
- )
2230
- };
2231
- },
2232
- createKindColumn() {
2233
- return {
2234
- title: "Kind",
2235
- field: "entity.kind"
2236
- };
2237
- },
2238
- createTypeColumn() {
2239
- return {
2240
- title: "Type",
2241
- field: "entity.spec.type"
2242
- };
2243
- }
2244
- };
2245
-
2246
- const DocsTable = (props) => {
2247
- const { entities, title, loading, columns, actions, options } = props;
2248
- const [, copyToClipboard] = useCopyToClipboard();
2249
- const getRouteToReaderPageFor = useRouteRef(rootDocsRouteRef);
2250
- const config = useApi(configApiRef);
2251
- if (!entities)
2252
- return null;
2253
- const documents = entities.map((entity) => {
2254
- var _a;
2255
- const ownedByRelations = getEntityRelations(entity, RELATION_OWNED_BY);
2256
- return {
2257
- entity,
2258
- resolved: {
2259
- docsUrl: getRouteToReaderPageFor({
2260
- namespace: toLowerMaybe(
2261
- (_a = entity.metadata.namespace) != null ? _a : "default",
2262
- config
2263
- ),
2264
- kind: toLowerMaybe(entity.kind, config),
2265
- name: toLowerMaybe(entity.metadata.name, config)
2266
- }),
2267
- ownedByRelations,
2268
- ownedByRelationsTitle: ownedByRelations.map((r) => humanizeEntityRef(r, { defaultKind: "group" })).join(", ")
2269
- }
2270
- };
2271
- });
2272
- const defaultColumns = [
2273
- columnFactories.createNameColumn(),
2274
- columnFactories.createOwnerColumn(),
2275
- columnFactories.createKindColumn(),
2276
- columnFactories.createTypeColumn()
2277
- ];
2278
- const defaultActions = [
2279
- actionFactories.createCopyDocsUrlAction(copyToClipboard)
2280
- ];
2281
- return /* @__PURE__ */ React.createElement(React.Fragment, null, loading || documents && documents.length > 0 ? /* @__PURE__ */ React.createElement(
2282
- Table,
2283
- {
2284
- isLoading: loading,
2285
- options: {
2286
- paging: true,
2287
- pageSize: 20,
2288
- search: true,
2289
- actionsColumnIndex: -1,
2290
- ...options
2291
- },
2292
- data: documents,
2293
- columns: columns || defaultColumns,
2294
- actions: actions || defaultActions,
2295
- title: title ? `${title} (${documents.length})` : `All (${documents.length})`
2296
- }
2297
- ) : /* @__PURE__ */ React.createElement(
2298
- EmptyState,
2299
- {
2300
- missing: "data",
2301
- title: "No documents to show",
2302
- description: "Create your own document. Check out our Getting Started Information",
2303
- action: /* @__PURE__ */ React.createElement(
2304
- LinkButton,
2305
- {
2306
- color: "primary",
2307
- to: "https://backstage.io/docs/features/techdocs/getting-started",
2308
- variant: "contained"
2309
- },
2310
- "DOCS"
2311
- )
2312
- }
2313
- ));
2314
- };
2315
- DocsTable.columns = columnFactories;
2316
- DocsTable.actions = actionFactories;
2317
-
2318
- const EntityListDocsTable = (props) => {
2319
- var _a, _b;
2320
- const { columns, actions, options } = props;
2321
- const { loading, error, entities, filters } = useEntityList();
2322
- const { isStarredEntity, toggleStarredEntity } = useStarredEntities();
2323
- const [, copyToClipboard] = useCopyToClipboard();
2324
- const title = capitalize((_b = (_a = filters.user) == null ? void 0 : _a.value) != null ? _b : "all");
2325
- const defaultActions = [
2326
- actionFactories.createCopyDocsUrlAction(copyToClipboard),
2327
- actionFactories.createStarEntityAction(
2328
- isStarredEntity,
2329
- toggleStarredEntity
2330
- )
2331
- ];
2332
- if (error) {
2333
- return /* @__PURE__ */ React.createElement(
2334
- WarningPanel,
2335
- {
2336
- severity: "error",
2337
- title: "Could not load available documentation."
2338
- },
2339
- /* @__PURE__ */ React.createElement(CodeSnippet, { language: "text", text: error.toString() })
2340
- );
2341
- }
2342
- return /* @__PURE__ */ React.createElement(
2343
- DocsTable,
2344
- {
2345
- title,
2346
- entities,
2347
- loading,
2348
- actions: actions || defaultActions,
2349
- columns,
2350
- options
2351
- }
2352
- );
2353
- };
2354
- EntityListDocsTable.columns = columnFactories;
2355
- EntityListDocsTable.actions = actionFactories;
2356
-
2357
- const TechDocsPageWrapper = (props) => {
2358
- var _a;
2359
- const { children } = props;
2360
- const configApi = useApi(configApiRef);
2361
- const generatedSubtitle = `Documentation available in ${(_a = configApi.getOptionalString("organization.name")) != null ? _a : "Backstage"}`;
2362
- return /* @__PURE__ */ React.createElement(
2363
- PageWithHeader,
2364
- {
2365
- title: "Documentation",
2366
- subtitle: generatedSubtitle,
2367
- themeId: "documentation"
2368
- },
2369
- children
2370
- );
2371
- };
2372
-
2373
- class TechDocsFilter {
2374
- getCatalogFilters() {
2375
- return {
2376
- "metadata.annotations.backstage.io/techdocs-ref": CATALOG_FILTER_EXISTS
2377
- };
2378
- }
2379
- }
2380
- const TechDocsPicker = () => {
2381
- const { updateFilters } = useEntityList();
2382
- useEffect(() => {
2383
- updateFilters({
2384
- techdocs: new TechDocsFilter()
2385
- });
2386
- }, [updateFilters]);
2387
- return null;
2388
- };
2389
-
2390
- const DefaultTechDocsHome = (props) => {
2391
- const { initialFilter = "owned", columns, actions } = props;
2392
- return /* @__PURE__ */ React.createElement(TechDocsPageWrapper, null, /* @__PURE__ */ React.createElement(Content, null, /* @__PURE__ */ React.createElement(ContentHeader, { title: "" }, /* @__PURE__ */ React.createElement(SupportButton, null, "Discover documentation in your ecosystem.")), /* @__PURE__ */ React.createElement(EntityListProvider, null, /* @__PURE__ */ React.createElement(CatalogFilterLayout, null, /* @__PURE__ */ React.createElement(CatalogFilterLayout.Filters, null, /* @__PURE__ */ React.createElement(TechDocsPicker, null), /* @__PURE__ */ React.createElement(UserListPicker, { initialFilter }), /* @__PURE__ */ React.createElement(EntityOwnerPicker, null), /* @__PURE__ */ React.createElement(EntityTagPicker, null)), /* @__PURE__ */ React.createElement(CatalogFilterLayout.Content, null, /* @__PURE__ */ React.createElement(EntityListDocsTable, { actions, columns }))))));
2393
- };
2394
-
2395
- const techdocsPlugin = createPlugin({
2396
- id: "techdocs",
2397
- apis: [
2398
- createApiFactory({
2399
- api: techdocsStorageApiRef$1,
2400
- deps: {
2401
- configApi: configApiRef,
2402
- discoveryApi: discoveryApiRef,
2403
- identityApi: identityApiRef,
2404
- fetchApi: fetchApiRef
2405
- },
2406
- factory: ({ configApi, discoveryApi, identityApi, fetchApi }) => new TechDocsStorageClient({
2407
- configApi,
2408
- discoveryApi,
2409
- identityApi,
2410
- fetchApi
2411
- })
2412
- }),
2413
- createApiFactory({
2414
- api: techdocsApiRef$1,
2415
- deps: {
2416
- configApi: configApiRef,
2417
- discoveryApi: discoveryApiRef,
2418
- fetchApi: fetchApiRef
2419
- },
2420
- factory: ({ configApi, discoveryApi, fetchApi }) => new TechDocsClient({
2421
- configApi,
2422
- discoveryApi,
2423
- fetchApi
2424
- })
2425
- })
2426
- ],
2427
- routes: {
2428
- root: rootRouteRef,
2429
- docRoot: rootDocsRouteRef,
2430
- entityContent: rootCatalogDocsRouteRef
2431
- }
2432
- });
2433
- const TechdocsPage = techdocsPlugin.provide(
2434
- createRoutableExtension({
2435
- name: "TechdocsPage",
2436
- component: () => Promise.resolve().then(function () { return Router$1; }).then((m) => m.Router),
2437
- mountPoint: rootRouteRef
2438
- })
2439
- );
2440
- const EntityTechdocsContent = techdocsPlugin.provide(
2441
- createRoutableExtension({
2442
- name: "EntityTechdocsContent",
2443
- component: () => Promise.resolve().then(function () { return Router$1; }).then((m) => m.EmbeddedDocsRouter),
2444
- mountPoint: rootCatalogDocsRouteRef
2445
- })
2446
- );
2447
- const TechDocsCustomHome = techdocsPlugin.provide(
2448
- createRoutableExtension({
2449
- name: "TechDocsCustomHome",
2450
- component: () => import('./TechDocsCustomHome-971e66b4.esm.js').then(
2451
- (m) => m.TechDocsCustomHome
2452
- ),
2453
- mountPoint: rootRouteRef
2454
- })
2455
- );
2456
- const TechDocsIndexPage$2 = techdocsPlugin.provide(
2457
- createRoutableExtension({
2458
- name: "TechDocsIndexPage",
2459
- component: () => Promise.resolve().then(function () { return TechDocsIndexPage$1; }).then(
2460
- (m) => m.TechDocsIndexPage
2461
- ),
2462
- mountPoint: rootRouteRef
2463
- })
2464
- );
2465
- const TechDocsReaderPage = techdocsPlugin.provide(
2466
- createRoutableExtension({
2467
- name: "TechDocsReaderPage",
2468
- component: () => import('./index-6fc556ea.esm.js').then(
2469
- (m) => m.TechDocsReaderPage
2470
- ),
2471
- mountPoint: rootDocsRouteRef
2472
- })
2473
- );
2474
- const TechDocsSearchResultListItem = techdocsPlugin.provide(
2475
- createSearchResultListItemExtension({
2476
- name: "TechDocsSearchResultListItem",
2477
- component: () => import('./TechDocsSearchResultListItem-4736f829.esm.js').then(
2478
- (m) => m.TechDocsSearchResultListItem
2479
- ),
2480
- predicate: (result) => result.type === "techdocs"
2481
- })
2482
- );
2483
-
2484
- const TECHDOCS_EXTERNAL_ANNOTATION$1 = "backstage.io/techdocs-entity";
2485
- const EntityPageDocs = ({ entity }) => {
2486
- var _a, _b;
2487
- let entityRef = getCompoundEntityRef(entity);
2488
- if ((_a = entity.metadata.annotations) == null ? void 0 : _a[TECHDOCS_EXTERNAL_ANNOTATION$1]) {
2489
- try {
2490
- entityRef = parseEntityRef(
2491
- (_b = entity.metadata.annotations) == null ? void 0 : _b[TECHDOCS_EXTERNAL_ANNOTATION$1]
2492
- );
2493
- } catch {
2494
- }
2495
- }
2496
- return /* @__PURE__ */ React.createElement(TechDocsReaderPage, { entityRef }, /* @__PURE__ */ React.createElement(TechDocsReaderPageSubheader, null), /* @__PURE__ */ React.createElement(TechDocsReaderPageContent, { withSearch: false }));
2497
- };
2498
-
2499
- const TechDocsIndexPage = (props) => {
2500
- const outlet = useOutlet();
2501
- return outlet || /* @__PURE__ */ React.createElement(DefaultTechDocsHome, { ...props });
2502
- };
2503
-
2504
- var TechDocsIndexPage$1 = /*#__PURE__*/Object.freeze({
2505
- __proto__: null,
2506
- TechDocsIndexPage: TechDocsIndexPage
2507
- });
2508
-
2509
- const TECHDOCS_ANNOTATION = "backstage.io/techdocs-ref";
2510
- const TECHDOCS_EXTERNAL_ANNOTATION = "backstage.io/techdocs-entity";
2511
- const isTechDocsAvailable = (entity) => {
2512
- var _a, _b, _c, _d;
2513
- return Boolean((_b = (_a = entity == null ? void 0 : entity.metadata) == null ? void 0 : _a.annotations) == null ? void 0 : _b[TECHDOCS_ANNOTATION]) || Boolean((_d = (_c = entity == null ? void 0 : entity.metadata) == null ? void 0 : _c.annotations) == null ? void 0 : _d[TECHDOCS_EXTERNAL_ANNOTATION]);
2514
- };
2515
- const Router = () => {
2516
- return /* @__PURE__ */ React.createElement(Routes, null, /* @__PURE__ */ React.createElement(Route, { path: "/", element: /* @__PURE__ */ React.createElement(TechDocsIndexPage, null) }), /* @__PURE__ */ React.createElement(
2517
- Route,
2518
- {
2519
- path: "/:namespace/:kind/:name/*",
2520
- element: /* @__PURE__ */ React.createElement(TechDocsReaderPage$1, null)
2521
- }
2522
- ));
2523
- };
2524
- const EmbeddedDocsRouter = (props) => {
2525
- var _a, _b;
2526
- const { children } = props;
2527
- const { entity } = useEntity();
2528
- const element = useRoutes([
2529
- {
2530
- path: "/*",
2531
- element: /* @__PURE__ */ React.createElement(EntityPageDocs, { entity }),
2532
- children: [
2533
- {
2534
- path: "*",
2535
- element: children
2536
- }
2537
- ]
2538
- }
2539
- ]);
2540
- const projectId = ((_a = entity.metadata.annotations) == null ? void 0 : _a[TECHDOCS_ANNOTATION]) || ((_b = entity.metadata.annotations) == null ? void 0 : _b[TECHDOCS_EXTERNAL_ANNOTATION]);
2541
- if (!projectId) {
2542
- return /* @__PURE__ */ React.createElement(MissingAnnotationEmptyState, { annotation: [TECHDOCS_ANNOTATION] });
2543
- }
2544
- return element;
2545
- };
2546
-
2547
- var Router$1 = /*#__PURE__*/Object.freeze({
2548
- __proto__: null,
2549
- isTechDocsAvailable: isTechDocsAvailable,
2550
- Router: Router,
2551
- EmbeddedDocsRouter: EmbeddedDocsRouter
2552
- });
2553
-
2554
- export { DocsTable as D, EntityTechdocsContent as E, Reader as R, TechDocsPageWrapper as T, DocsCardGrid as a, TechDocsReaderPage$1 as b, TechDocsReaderLayout as c, TechDocsCustomHome as d, TechDocsIndexPage$2 as e, TechdocsPage as f, TechDocsReaderPage as g, TechDocsSearchResultListItem as h, techdocsStorageApiRef as i, techdocsApiRef as j, TechDocsClient as k, TechDocsStorageClient as l, TechDocsReaderProvider as m, TechDocsReaderPageHeader as n, TechDocsReaderPageContent as o, TechDocsReaderPageSubheader as p, TechDocsSearch as q, EntityListDocsGrid as r, EntityListDocsTable as s, techdocsPlugin as t, DefaultTechDocsHome as u, TechDocsPicker as v, isTechDocsAvailable as w, Router as x, EmbeddedDocsRouter as y };
2555
- //# sourceMappingURL=index-71d2c378.esm.js.map
1903
+ export { Reader as R, TechDocsReaderPageSubheader as T, TechDocsReaderPageContent as a, TechDocsReaderPage as b, TechDocsReaderProvider as c, TechDocsReaderLayout as d, TechDocsReaderPageHeader as e, TechDocsSearch as f };
1904
+ //# sourceMappingURL=TechDocsReaderPage-ac1d5884.esm.js.map