@axis-backstage/plugin-readme 0.18.2 → 0.19.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.
- package/CHANGELOG.md +38 -0
- package/README.md +11 -7
- package/dist/alpha.d.ts +7 -3
- package/dist/alpha.esm.js +50 -3
- package/dist/alpha.esm.js.map +1 -1
- package/dist/components/ReadmeCard/ReadmeCard.esm.js +62 -6
- package/dist/components/ReadmeCard/ReadmeCard.esm.js.map +1 -1
- package/dist/components/ReadmeContent/ReadmeContent.esm.js +42 -0
- package/dist/components/ReadmeContent/ReadmeContent.esm.js.map +1 -0
- package/dist/components/ReadmeDialog/ReadmeDialog.esm.js +3 -3
- package/dist/components/ReadmeDialog/ReadmeDialog.esm.js.map +1 -1
- package/dist/hooks/useFullViewParam.esm.js +14 -14
- package/dist/hooks/useFullViewParam.esm.js.map +1 -1
- package/dist/hooks/useReadmeContent.esm.js +29 -0
- package/dist/hooks/useReadmeContent.esm.js.map +1 -0
- package/dist/index.d.ts +15 -4
- package/package.json +3 -2
- package/dist/components/FetchComponent/FetchComponent.esm.js +0 -65
- package/dist/components/FetchComponent/FetchComponent.esm.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,43 @@
|
|
|
1
1
|
# @axis-backstage/plugin-readme
|
|
2
2
|
|
|
3
|
+
## 0.19.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- b5e5411: Added a `hideIfNotFound` config option to the readme card extension for
|
|
8
|
+
the new frontend system. When set to `true`, the entire card is hidden
|
|
9
|
+
when no README.md file is found, instead of showing an error message.
|
|
10
|
+
This is the NFS equivalent of the `isReadmeAvailable` entity filter
|
|
11
|
+
available in the old frontend system.
|
|
12
|
+
|
|
13
|
+
Example app-config.yaml:
|
|
14
|
+
|
|
15
|
+
app:
|
|
16
|
+
extensions: - entity-card:readme:
|
|
17
|
+
config:
|
|
18
|
+
hideIfNotFound: true
|
|
19
|
+
|
|
20
|
+
- bb83b10: Migrated `ReadmeCard` from the legacy MUI `InfoCard` to `EntityInfoCard` from `@backstage/plugin-catalog-react`.
|
|
21
|
+
|
|
22
|
+
The `variant` and `maxHeight` props have been removed from `ReadmeCardProps`. Users relying on these props should switch to `ReadmeCardLegacyProps`, which preserves the old behaviour and is accepted by `ReadmeCard` for backward compatibility. Both `variant` and `maxHeight` are deprecated and will be removed in a future release.
|
|
23
|
+
|
|
24
|
+
### Patch Changes
|
|
25
|
+
|
|
26
|
+
- 8a8e8da: NFS: Make the Readme entity card height configurable
|
|
27
|
+
|
|
28
|
+
The height of the entity card now defaults to the same height as
|
|
29
|
+
the legacy card. If used in NFS the height can be configured
|
|
30
|
+
using the config as follows:
|
|
31
|
+
|
|
32
|
+
```yaml
|
|
33
|
+
app:
|
|
34
|
+
extensions:
|
|
35
|
+
- entity-card:readme:
|
|
36
|
+
config:
|
|
37
|
+
hideIfNotFound: true
|
|
38
|
+
maxHeight: 500px
|
|
39
|
+
```
|
|
40
|
+
|
|
3
41
|
## 0.18.2
|
|
4
42
|
|
|
5
43
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -66,7 +66,9 @@ const defaultEntityPage = (
|
|
|
66
66
|
)
|
|
67
67
|
```
|
|
68
68
|
|
|
69
|
-
|
|
69
|
+
> **Note:** `isReadmeAvailable` is only available in the legacy frontend system. In the new frontend system, use the `hideIfNotFound` config option instead — see the [New Frontend System](#new-frontend-system) section below.
|
|
70
|
+
|
|
71
|
+
To use `ReadmeCard` in a separate page with full height:
|
|
70
72
|
|
|
71
73
|
```tsx
|
|
72
74
|
import { ReadmeCard } from '@axis-backstage/plugin-readme';
|
|
@@ -104,19 +106,21 @@ const app = createApp({
|
|
|
104
106
|
});
|
|
105
107
|
```
|
|
106
108
|
|
|
107
|
-
3. If necessary, you can also customize the plugin:
|
|
109
|
+
3. If necessary, you can also customize the plugin using the following config options:
|
|
110
|
+
|
|
111
|
+
| Config key | Type | Default | Description |
|
|
112
|
+
| ---------------- | --------- | --------- | ----------------------------------------------------------------------------------------------------------------------------- |
|
|
113
|
+
| `hideIfNotFound` | `boolean` | `false` | When `true`, the card is hidden entirely if no README file is found, instead of showing an informational message. |
|
|
114
|
+
| `maxHeight` | `string` | `'235px'` | Maximum height of the card content area. Accepts any valid CSS length (e.g. `'500px'`, `'50vh'`) or `'none'` for full height. |
|
|
108
115
|
|
|
109
116
|
```yaml
|
|
110
117
|
# app-config.yaml
|
|
111
118
|
app:
|
|
112
|
-
# ...
|
|
113
119
|
extensions:
|
|
114
|
-
# ...
|
|
115
120
|
- entity-card:readme:
|
|
116
121
|
config:
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
# ...
|
|
122
|
+
hideIfNotFound: true
|
|
123
|
+
maxHeight: 500px
|
|
120
124
|
```
|
|
121
125
|
|
|
122
126
|
## Layout
|
package/dist/alpha.d.ts
CHANGED
|
@@ -20,15 +20,17 @@ declare const _default: _backstage_frontend_plugin_api.OverridableFrontendPlugin
|
|
|
20
20
|
params: <TApi, TImpl extends TApi, TDeps extends { [name in string]: unknown; }>(params: _backstage_frontend_plugin_api.ApiFactory<TApi, TImpl, TDeps>) => _backstage_frontend_plugin_api.ExtensionBlueprintParams<_backstage_frontend_plugin_api.AnyApiFactory>;
|
|
21
21
|
}>;
|
|
22
22
|
"entity-card:readme": _backstage_frontend_plugin_api.OverridableExtensionDefinition<{
|
|
23
|
-
kind: "entity-card";
|
|
24
|
-
name: undefined;
|
|
25
23
|
config: {
|
|
24
|
+
hideIfNotFound: boolean;
|
|
25
|
+
maxHeight: string;
|
|
26
26
|
filter: _backstage_filter_predicates.FilterPredicate | undefined;
|
|
27
27
|
type: "content" | "info" | undefined;
|
|
28
28
|
};
|
|
29
29
|
configInput: {
|
|
30
|
+
hideIfNotFound?: boolean | undefined;
|
|
31
|
+
maxHeight?: string | undefined;
|
|
30
32
|
filter?: _backstage_filter_predicates.FilterPredicate | undefined;
|
|
31
|
-
type?: "content" | "info" | undefined;
|
|
33
|
+
type?: "content" | "info" | undefined | undefined;
|
|
32
34
|
};
|
|
33
35
|
output: _backstage_frontend_plugin_api.ExtensionDataRef<react.JSX.Element, "core.reactElement", {}> | _backstage_frontend_plugin_api.ExtensionDataRef<(entity: _backstage_catalog_model.Entity) => boolean, "catalog.entity-filter-function", {
|
|
34
36
|
optional: true;
|
|
@@ -38,6 +40,8 @@ declare const _default: _backstage_frontend_plugin_api.OverridableFrontendPlugin
|
|
|
38
40
|
optional: true;
|
|
39
41
|
}>;
|
|
40
42
|
inputs: {};
|
|
43
|
+
kind: "entity-card";
|
|
44
|
+
name: undefined;
|
|
41
45
|
params: {
|
|
42
46
|
loader: () => Promise<JSX.Element>;
|
|
43
47
|
filter?: string | _backstage_filter_predicates.FilterPredicate | ((entity: _backstage_catalog_model.Entity) => boolean);
|
package/dist/alpha.esm.js
CHANGED
|
@@ -3,6 +3,7 @@ import { ApiBlueprint, identityApiRef, fetchApiRef, discoveryApiRef, createFront
|
|
|
3
3
|
import { readmeApiRef } from './api/ReadmeApi.esm.js';
|
|
4
4
|
import { ReadmeClient } from './api/ReadmeClient.esm.js';
|
|
5
5
|
import { EntityCardBlueprint } from '@backstage/plugin-catalog-react/alpha';
|
|
6
|
+
import { z } from 'zod';
|
|
6
7
|
|
|
7
8
|
const readmeApi = ApiBlueprint.make({
|
|
8
9
|
params: (defineParams) => defineParams({
|
|
@@ -15,9 +16,55 @@ const readmeApi = ApiBlueprint.make({
|
|
|
15
16
|
factory: ({ discoveryApi, fetchApi, identityApi }) => new ReadmeClient({ discoveryApi, fetchApi, identityApi })
|
|
16
17
|
})
|
|
17
18
|
});
|
|
18
|
-
const readmeCard = EntityCardBlueprint.
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
const readmeCard = EntityCardBlueprint.makeWithOverrides({
|
|
20
|
+
configSchema: {
|
|
21
|
+
/**
|
|
22
|
+
* When set to `true`, the entire README card is hidden if no README file
|
|
23
|
+
* is found for the entity (i.e. the backend returns a 404).
|
|
24
|
+
*
|
|
25
|
+
* Defaults to `false`, which shows an informational message with a link
|
|
26
|
+
* to the documentation instead.
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* ```yaml
|
|
30
|
+
* # app-config.yaml
|
|
31
|
+
* app:
|
|
32
|
+
* extensions:
|
|
33
|
+
* - entity-card:readme:
|
|
34
|
+
* config:
|
|
35
|
+
* hideIfNotFound: true
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
hideIfNotFound: z.boolean().default(false),
|
|
39
|
+
/**
|
|
40
|
+
* Sets the maximum height of the README card content area. Accepts any
|
|
41
|
+
* valid CSS length value (e.g. `'235px'`, `'50vh'`) or `'none'` to show
|
|
42
|
+
* the full content without scrolling.
|
|
43
|
+
*
|
|
44
|
+
* Defaults to `'235px'`.
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* ```yaml
|
|
48
|
+
* # app-config.yaml
|
|
49
|
+
* app:
|
|
50
|
+
* extensions:
|
|
51
|
+
* - entity-card:readme:
|
|
52
|
+
* config:
|
|
53
|
+
* maxHeight: 500px
|
|
54
|
+
* ```
|
|
55
|
+
*/
|
|
56
|
+
maxHeight: z.string().default("235px")
|
|
57
|
+
},
|
|
58
|
+
factory(originalFactory, { config }) {
|
|
59
|
+
return originalFactory({
|
|
60
|
+
loader: async () => import('./components/ReadmeCard/index.esm.js').then((m) => /* @__PURE__ */ jsx(
|
|
61
|
+
m.ReadmeCard,
|
|
62
|
+
{
|
|
63
|
+
hideIfNotFound: config.hideIfNotFound,
|
|
64
|
+
maxHeight: config.maxHeight
|
|
65
|
+
}
|
|
66
|
+
))
|
|
67
|
+
});
|
|
21
68
|
}
|
|
22
69
|
});
|
|
23
70
|
var alpha = createFrontendPlugin({
|
package/dist/alpha.esm.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"alpha.esm.js","sources":["../src/alpha.tsx"],"sourcesContent":["/**\n * Frontend plugin that fetches and displays the readme for an entity\n *\n * @packageDocumentation\n */\n\nimport {\n ApiBlueprint,\n createFrontendPlugin,\n discoveryApiRef,\n fetchApiRef,\n identityApiRef,\n} from '@backstage/frontend-plugin-api';\nimport { readmeApiRef } from './api/ReadmeApi';\nimport { ReadmeClient } from './api/ReadmeClient';\nimport { EntityCardBlueprint } from '@backstage/plugin-catalog-react/alpha';\n\nconst readmeApi = ApiBlueprint.make({\n params: defineParams =>\n defineParams({\n api: readmeApiRef,\n deps: {\n discoveryApi: discoveryApiRef,\n fetchApi: fetchApiRef,\n identityApi: identityApiRef,\n },\n factory: ({ discoveryApi, fetchApi, identityApi }) =>\n new ReadmeClient({ discoveryApi, fetchApi, identityApi }),\n }),\n});\n\nconst readmeCard = EntityCardBlueprint.
|
|
1
|
+
{"version":3,"file":"alpha.esm.js","sources":["../src/alpha.tsx"],"sourcesContent":["/**\n * Frontend plugin that fetches and displays the readme for an entity\n *\n * @packageDocumentation\n */\n\nimport {\n ApiBlueprint,\n createFrontendPlugin,\n discoveryApiRef,\n fetchApiRef,\n identityApiRef,\n} from '@backstage/frontend-plugin-api';\nimport { readmeApiRef } from './api/ReadmeApi';\nimport { ReadmeClient } from './api/ReadmeClient';\nimport { EntityCardBlueprint } from '@backstage/plugin-catalog-react/alpha';\nimport { z } from 'zod';\n\nconst readmeApi = ApiBlueprint.make({\n params: defineParams =>\n defineParams({\n api: readmeApiRef,\n deps: {\n discoveryApi: discoveryApiRef,\n fetchApi: fetchApiRef,\n identityApi: identityApiRef,\n },\n factory: ({ discoveryApi, fetchApi, identityApi }) =>\n new ReadmeClient({ discoveryApi, fetchApi, identityApi }),\n }),\n});\n\nconst readmeCard = EntityCardBlueprint.makeWithOverrides({\n configSchema: {\n /**\n * When set to `true`, the entire README card is hidden if no README file\n * is found for the entity (i.e. the backend returns a 404).\n *\n * Defaults to `false`, which shows an informational message with a link\n * to the documentation instead.\n *\n * @example\n * ```yaml\n * # app-config.yaml\n * app:\n * extensions:\n * - entity-card:readme:\n * config:\n * hideIfNotFound: true\n * ```\n */\n hideIfNotFound: z.boolean().default(false),\n /**\n * Sets the maximum height of the README card content area. Accepts any\n * valid CSS length value (e.g. `'235px'`, `'50vh'`) or `'none'` to show\n * the full content without scrolling.\n *\n * Defaults to `'235px'`.\n *\n * @example\n * ```yaml\n * # app-config.yaml\n * app:\n * extensions:\n * - entity-card:readme:\n * config:\n * maxHeight: 500px\n * ```\n */\n maxHeight: z.string().default('235px'),\n },\n factory(originalFactory, { config }) {\n return originalFactory({\n loader: async () =>\n import('./components/ReadmeCard').then(m => (\n <m.ReadmeCard\n hideIfNotFound={config.hideIfNotFound}\n maxHeight={config.maxHeight}\n />\n )),\n });\n },\n});\n\nexport default createFrontendPlugin({\n pluginId: 'readme',\n extensions: [readmeApi, readmeCard],\n});\n"],"names":[],"mappings":";;;;;;;AAkBA,MAAM,SAAA,GAAY,aAAa,IAAA,CAAK;AAAA,EAClC,MAAA,EAAQ,kBACN,YAAA,CAAa;AAAA,IACX,GAAA,EAAK,YAAA;AAAA,IACL,IAAA,EAAM;AAAA,MACJ,YAAA,EAAc,eAAA;AAAA,MACd,QAAA,EAAU,WAAA;AAAA,MACV,WAAA,EAAa;AAAA,KACf;AAAA,IACA,OAAA,EAAS,CAAC,EAAE,YAAA,EAAc,QAAA,EAAU,WAAA,EAAY,KAC9C,IAAI,YAAA,CAAa,EAAE,YAAA,EAAc,QAAA,EAAU,aAAa;AAAA,GAC3D;AACL,CAAC,CAAA;AAED,MAAM,UAAA,GAAa,oBAAoB,iBAAA,CAAkB;AAAA,EACvD,YAAA,EAAc;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAkBZ,cAAA,EAAgB,CAAA,CAAE,OAAA,EAAQ,CAAE,QAAQ,KAAK,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAkBzC,SAAA,EAAW,CAAA,CAAE,MAAA,EAAO,CAAE,QAAQ,OAAO;AAAA,GACvC;AAAA,EACA,OAAA,CAAQ,eAAA,EAAiB,EAAE,MAAA,EAAO,EAAG;AACnC,IAAA,OAAO,eAAA,CAAgB;AAAA,MACrB,QAAQ,YACN,OAAO,sCAAyB,CAAA,CAAE,KAAK,CAAA,CAAA,qBACrC,GAAA;AAAA,QAAC,CAAA,CAAE,UAAA;AAAA,QAAF;AAAA,UACC,gBAAgB,MAAA,CAAO,cAAA;AAAA,UACvB,WAAW,MAAA,CAAO;AAAA;AAAA,OAErB;AAAA,KACJ,CAAA;AAAA,EACH;AACF,CAAC,CAAA;AAED,YAAe,oBAAA,CAAqB;AAAA,EAClC,QAAA,EAAU,QAAA;AAAA,EACV,UAAA,EAAY,CAAC,SAAA,EAAW,UAAU;AACpC,CAAC,CAAA;;;;"}
|
|
@@ -1,16 +1,30 @@
|
|
|
1
|
-
import { jsxs, Fragment
|
|
1
|
+
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
2
2
|
import IconButton from '@mui/material/IconButton';
|
|
3
3
|
import Box from '@mui/material/Box';
|
|
4
4
|
import FullscreenIcon from '@mui/icons-material/Fullscreen';
|
|
5
|
+
import { EntityInfoCard } from '@backstage/plugin-catalog-react';
|
|
5
6
|
import { InfoCard } from '@backstage/core-components';
|
|
6
|
-
import {
|
|
7
|
+
import { ReadmeContent } from '../ReadmeContent/ReadmeContent.esm.js';
|
|
7
8
|
import { ReadmeDialog } from '../ReadmeDialog/ReadmeDialog.esm.js';
|
|
8
9
|
import { useFullViewParam } from '../../hooks/useFullViewParam.esm.js';
|
|
10
|
+
import { useReadmeContent } from '../../hooks/useReadmeContent.esm.js';
|
|
11
|
+
import { ResponseError } from '@backstage/errors';
|
|
9
12
|
|
|
10
|
-
|
|
11
|
-
|
|
13
|
+
function isLegacyProps(props) {
|
|
14
|
+
return "variant" in props;
|
|
15
|
+
}
|
|
16
|
+
function ReadmeCardLegacy(props) {
|
|
17
|
+
const {
|
|
18
|
+
variant = "gridItem",
|
|
19
|
+
maxHeight: propMaxHeight,
|
|
20
|
+
hideIfNotFound
|
|
21
|
+
} = props;
|
|
12
22
|
const [isFullViewOpen, setIsFullViewOpen] = useFullViewParam();
|
|
23
|
+
const readmeContent = useReadmeContent();
|
|
13
24
|
const maxHeight = variant === "fullHeight" ? "none" : propMaxHeight ?? "235px";
|
|
25
|
+
if (hideIfNotFound && readmeContent.error instanceof ResponseError && readmeContent.error.statusCode === 404) {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
14
28
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
15
29
|
/* @__PURE__ */ jsx(
|
|
16
30
|
InfoCard,
|
|
@@ -27,17 +41,59 @@ const ReadmeCard = (props) => {
|
|
|
27
41
|
children: /* @__PURE__ */ jsx(FullscreenIcon, {})
|
|
28
42
|
}
|
|
29
43
|
) : void 0,
|
|
30
|
-
children: /* @__PURE__ */ jsx("div", { style: { overflow: "auto" }, children: /* @__PURE__ */ jsx(Box, { maxHeight, children: /* @__PURE__ */ jsx(
|
|
44
|
+
children: /* @__PURE__ */ jsx("div", { style: { overflow: "auto" }, children: /* @__PURE__ */ jsx(Box, { maxHeight, children: /* @__PURE__ */ jsx(ReadmeContent, { ...readmeContent }) }) })
|
|
45
|
+
}
|
|
46
|
+
),
|
|
47
|
+
/* @__PURE__ */ jsx(
|
|
48
|
+
ReadmeDialog,
|
|
49
|
+
{
|
|
50
|
+
open: isFullViewOpen,
|
|
51
|
+
onClose: () => setIsFullViewOpen(false),
|
|
52
|
+
...readmeContent
|
|
53
|
+
}
|
|
54
|
+
)
|
|
55
|
+
] });
|
|
56
|
+
}
|
|
57
|
+
function ReadmeCardNew(props) {
|
|
58
|
+
const { hideIfNotFound, maxHeight = "235px" } = props;
|
|
59
|
+
const [isFullViewOpen, setIsFullViewOpen] = useFullViewParam();
|
|
60
|
+
const readmeContent = useReadmeContent();
|
|
61
|
+
if (hideIfNotFound && readmeContent.error instanceof ResponseError && readmeContent.error.statusCode === 404) {
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
65
|
+
/* @__PURE__ */ jsx(
|
|
66
|
+
EntityInfoCard,
|
|
67
|
+
{
|
|
68
|
+
title: "README",
|
|
69
|
+
headerActions: /* @__PURE__ */ jsx(
|
|
70
|
+
IconButton,
|
|
71
|
+
{
|
|
72
|
+
onClick: () => setIsFullViewOpen(true),
|
|
73
|
+
"aria-label": "Open full view",
|
|
74
|
+
title: "Open full view",
|
|
75
|
+
size: "large",
|
|
76
|
+
children: /* @__PURE__ */ jsx(FullscreenIcon, {})
|
|
77
|
+
}
|
|
78
|
+
),
|
|
79
|
+
children: /* @__PURE__ */ jsx("div", { style: { overflow: "auto", maxHeight }, children: /* @__PURE__ */ jsx(ReadmeContent, { ...readmeContent }) })
|
|
31
80
|
}
|
|
32
81
|
),
|
|
33
82
|
/* @__PURE__ */ jsx(
|
|
34
83
|
ReadmeDialog,
|
|
35
84
|
{
|
|
36
85
|
open: isFullViewOpen,
|
|
37
|
-
onClose: () => setIsFullViewOpen(false)
|
|
86
|
+
onClose: () => setIsFullViewOpen(false),
|
|
87
|
+
...readmeContent
|
|
38
88
|
}
|
|
39
89
|
)
|
|
40
90
|
] });
|
|
91
|
+
}
|
|
92
|
+
const ReadmeCard = (props) => {
|
|
93
|
+
if (isLegacyProps(props)) {
|
|
94
|
+
return /* @__PURE__ */ jsx(ReadmeCardLegacy, { ...props });
|
|
95
|
+
}
|
|
96
|
+
return /* @__PURE__ */ jsx(ReadmeCardNew, { ...props });
|
|
41
97
|
};
|
|
42
98
|
|
|
43
99
|
export { ReadmeCard };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ReadmeCard.esm.js","sources":["../../../src/components/ReadmeCard/ReadmeCard.tsx"],"sourcesContent":["import IconButton from '@mui/material/IconButton';\nimport Box from '@mui/material/Box';\nimport FullscreenIcon from '@mui/icons-material/Fullscreen';\nimport { InfoCard, InfoCardVariants } from '@backstage/core-components';\nimport {
|
|
1
|
+
{"version":3,"file":"ReadmeCard.esm.js","sources":["../../../src/components/ReadmeCard/ReadmeCard.tsx"],"sourcesContent":["import IconButton from '@mui/material/IconButton';\nimport Box from '@mui/material/Box';\nimport FullscreenIcon from '@mui/icons-material/Fullscreen';\nimport { EntityInfoCard } from '@backstage/plugin-catalog-react';\nimport { InfoCard, InfoCardVariants } from '@backstage/core-components';\nimport { ReadmeContent } from '../ReadmeContent';\nimport { ReadmeDialog } from '../ReadmeDialog/ReadmeDialog';\nimport { useFullViewParam } from '../../hooks/useFullViewParam';\nimport { useReadmeContent } from '../../hooks/useReadmeContent';\nimport { ResponseError } from '@backstage/errors';\n\n/**\n * Props for the ReadmeCard component.\n * @public\n */\nexport type ReadmeCardProps = {\n hideIfNotFound?: boolean;\n maxHeight?: string | number;\n};\n\n/**\n * Props for the legacy MUI-based rendering of ReadmeCard.\n * @deprecated Use {@link ReadmeCardProps} instead.\n * @public\n */\nexport type ReadmeCardLegacyProps = {\n /** @deprecated Use the new {@link ReadmeCardProps} instead. */\n variant?: InfoCardVariants;\n /** @deprecated Use the new {@link ReadmeCardProps} instead. */\n maxHeight?: string | number;\n hideIfNotFound?: boolean;\n};\n\nfunction isLegacyProps(\n props: ReadmeCardProps | ReadmeCardLegacyProps,\n): props is ReadmeCardLegacyProps {\n return 'variant' in props;\n}\n\nfunction ReadmeCardLegacy(props: ReadmeCardLegacyProps) {\n const {\n variant = 'gridItem',\n maxHeight: propMaxHeight,\n hideIfNotFound,\n } = props;\n const [isFullViewOpen, setIsFullViewOpen] = useFullViewParam();\n const readmeContent = useReadmeContent();\n\n const maxHeight =\n variant === 'fullHeight' ? 'none' : propMaxHeight ?? '235px';\n\n if (\n hideIfNotFound &&\n readmeContent.error instanceof ResponseError &&\n readmeContent.error.statusCode === 404\n ) {\n return null;\n }\n\n return (\n <>\n <InfoCard\n title=\"README\"\n variant={variant}\n action={\n variant !== 'fullHeight' ? (\n <IconButton\n onClick={() => setIsFullViewOpen(true)}\n aria-label=\"Open full view\"\n title=\"Open full view\"\n size=\"large\"\n >\n <FullscreenIcon />\n </IconButton>\n ) : undefined\n }\n >\n <div style={{ overflow: 'auto' }}>\n <Box maxHeight={maxHeight}>\n <ReadmeContent {...readmeContent} />\n </Box>\n </div>\n </InfoCard>\n\n <ReadmeDialog\n open={isFullViewOpen}\n onClose={() => setIsFullViewOpen(false)}\n {...readmeContent}\n />\n </>\n );\n}\n\nfunction ReadmeCardNew(props: ReadmeCardProps) {\n const { hideIfNotFound, maxHeight = '235px' } = props;\n const [isFullViewOpen, setIsFullViewOpen] = useFullViewParam();\n const readmeContent = useReadmeContent();\n\n if (\n hideIfNotFound &&\n readmeContent.error instanceof ResponseError &&\n readmeContent.error.statusCode === 404\n ) {\n return null;\n }\n\n return (\n <>\n <EntityInfoCard\n title=\"README\"\n headerActions={\n <IconButton\n onClick={() => setIsFullViewOpen(true)}\n aria-label=\"Open full view\"\n title=\"Open full view\"\n size=\"large\"\n >\n <FullscreenIcon />\n </IconButton>\n }\n >\n <div style={{ overflow: 'auto', maxHeight }}>\n <ReadmeContent {...readmeContent} />\n </div>\n </EntityInfoCard>\n\n <ReadmeDialog\n open={isFullViewOpen}\n onClose={() => setIsFullViewOpen(false)}\n {...readmeContent}\n />\n </>\n );\n}\n\nexport const ReadmeCard = (props: ReadmeCardProps | ReadmeCardLegacyProps) => {\n if (isLegacyProps(props)) {\n return <ReadmeCardLegacy {...props} />;\n }\n return <ReadmeCardNew {...props} />;\n};\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
2
|
+
import { Progress, ResponseErrorPanel, ErrorPanel, MarkdownContent, CodeSnippet } from '@backstage/core-components';
|
|
3
|
+
import Box from '@mui/material/Box';
|
|
4
|
+
import Typography from '@mui/material/Typography';
|
|
5
|
+
import { ResponseError } from '@backstage/errors';
|
|
6
|
+
|
|
7
|
+
const ReadmeContent = ({
|
|
8
|
+
content,
|
|
9
|
+
loading,
|
|
10
|
+
error,
|
|
11
|
+
location
|
|
12
|
+
}) => {
|
|
13
|
+
if (loading) {
|
|
14
|
+
return /* @__PURE__ */ jsx(Progress, {});
|
|
15
|
+
}
|
|
16
|
+
if (error instanceof ResponseError) {
|
|
17
|
+
if (error.statusCode === 404) {
|
|
18
|
+
return /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsxs(Typography, { pb: 2, variant: "body2", children: [
|
|
19
|
+
"No README.md file found at source location:",
|
|
20
|
+
" ",
|
|
21
|
+
location && /* @__PURE__ */ jsx("strong", { children: location })
|
|
22
|
+
] }) });
|
|
23
|
+
}
|
|
24
|
+
return /* @__PURE__ */ jsx(ResponseErrorPanel, { error });
|
|
25
|
+
}
|
|
26
|
+
if (error) {
|
|
27
|
+
return /* @__PURE__ */ jsx(ErrorPanel, { error });
|
|
28
|
+
}
|
|
29
|
+
if (!content) {
|
|
30
|
+
return /* @__PURE__ */ jsx(ErrorPanel, { error: Error("Unknown error") });
|
|
31
|
+
}
|
|
32
|
+
if (!content[1] || content[1] === "error") {
|
|
33
|
+
return /* @__PURE__ */ jsx(ErrorPanel, { error: Error(content[1] ?? "Unknown error") });
|
|
34
|
+
}
|
|
35
|
+
if (content[1].startsWith("text/markdown")) {
|
|
36
|
+
return /* @__PURE__ */ jsx(MarkdownContent, { content: content[0] });
|
|
37
|
+
}
|
|
38
|
+
return /* @__PURE__ */ jsx("div", { "data-testid": "readme-content", children: /* @__PURE__ */ jsx(CodeSnippet, { text: content[0], language: "plaintext" }) });
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export { ReadmeContent };
|
|
42
|
+
//# sourceMappingURL=ReadmeContent.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ReadmeContent.esm.js","sources":["../../../src/components/ReadmeContent/ReadmeContent.tsx"],"sourcesContent":["import {\n CodeSnippet,\n ErrorPanel,\n MarkdownContent,\n Progress,\n ResponseErrorPanel,\n} from '@backstage/core-components';\nimport Box from '@mui/material/Box';\nimport Typography from '@mui/material/Typography';\nimport { ResponseError } from '@backstage/errors';\nimport type { UseReadmeContentResult } from '../../hooks/useReadmeContent';\n\nexport type ReadmeContentProps = UseReadmeContentResult;\n\nexport const ReadmeContent = ({\n content,\n loading,\n error,\n location,\n}: ReadmeContentProps) => {\n if (loading) {\n return <Progress />;\n }\n\n if (error instanceof ResponseError) {\n if (error.statusCode === 404) {\n return (\n <Box>\n <Typography pb={2} variant=\"body2\">\n No README.md file found at source location:{' '}\n {location && <strong>{location}</strong>}\n </Typography>\n </Box>\n );\n }\n return <ResponseErrorPanel error={error} />;\n }\n\n if (error) {\n return <ErrorPanel error={error} />;\n }\n if (!content) {\n return <ErrorPanel error={Error('Unknown error')} />;\n }\n if (!content[1] || content[1] === 'error') {\n return <ErrorPanel error={Error(content[1] ?? 'Unknown error')} />;\n }\n if (content[1].startsWith('text/markdown')) {\n return <MarkdownContent content={content[0]} />;\n }\n // probably text/plain\n return (\n <div data-testid=\"readme-content\">\n <CodeSnippet text={content[0]} language=\"plaintext\" />\n </div>\n );\n};\n"],"names":[],"mappings":";;;;;;AAcO,MAAM,gBAAgB,CAAC;AAAA,EAC5B,OAAA;AAAA,EACA,OAAA;AAAA,EACA,KAAA;AAAA,EACA;AACF,CAAA,KAA0B;AACxB,EAAA,IAAI,OAAA,EAAS;AACX,IAAA,2BAAQ,QAAA,EAAA,EAAS,CAAA;AAAA,EACnB;AAEA,EAAA,IAAI,iBAAiB,aAAA,EAAe;AAClC,IAAA,IAAI,KAAA,CAAM,eAAe,GAAA,EAAK;AAC5B,MAAA,2BACG,GAAA,EAAA,EACC,QAAA,kBAAA,IAAA,CAAC,cAAW,EAAA,EAAI,CAAA,EAAG,SAAQ,OAAA,EAAQ,QAAA,EAAA;AAAA,QAAA,6CAAA;AAAA,QACW,GAAA;AAAA,QAC3C,QAAA,oBAAY,GAAA,CAAC,QAAA,EAAA,EAAQ,QAAA,EAAA,QAAA,EAAS;AAAA,OAAA,EACjC,CAAA,EACF,CAAA;AAAA,IAEJ;AACA,IAAA,uBAAO,GAAA,CAAC,sBAAmB,KAAA,EAAc,CAAA;AAAA,EAC3C;AAEA,EAAA,IAAI,KAAA,EAAO;AACT,IAAA,uBAAO,GAAA,CAAC,cAAW,KAAA,EAAc,CAAA;AAAA,EACnC;AACA,EAAA,IAAI,CAAC,OAAA,EAAS;AACZ,IAAA,uBAAO,GAAA,CAAC,UAAA,EAAA,EAAW,KAAA,EAAO,KAAA,CAAM,eAAe,CAAA,EAAG,CAAA;AAAA,EACpD;AACA,EAAA,IAAI,CAAC,OAAA,CAAQ,CAAC,KAAK,OAAA,CAAQ,CAAC,MAAM,OAAA,EAAS;AACzC,IAAA,uBAAO,GAAA,CAAC,cAAW,KAAA,EAAO,KAAA,CAAM,QAAQ,CAAC,CAAA,IAAK,eAAe,CAAA,EAAG,CAAA;AAAA,EAClE;AACA,EAAA,IAAI,OAAA,CAAQ,CAAC,CAAA,CAAE,UAAA,CAAW,eAAe,CAAA,EAAG;AAC1C,IAAA,uBAAO,GAAA,CAAC,eAAA,EAAA,EAAgB,OAAA,EAAS,OAAA,CAAQ,CAAC,CAAA,EAAG,CAAA;AAAA,EAC/C;AAEA,EAAA,uBACE,GAAA,CAAC,KAAA,EAAA,EAAI,aAAA,EAAY,gBAAA,EACf,QAAA,kBAAA,GAAA,CAAC,WAAA,EAAA,EAAY,IAAA,EAAM,OAAA,CAAQ,CAAC,CAAA,EAAG,QAAA,EAAS,WAAA,EAAY,CAAA,EACtD,CAAA;AAEJ;;;;"}
|
|
@@ -5,9 +5,9 @@ import DialogActions from '@mui/material/DialogActions';
|
|
|
5
5
|
import DialogContent from '@mui/material/DialogContent';
|
|
6
6
|
import DialogTitle from '@mui/material/DialogTitle';
|
|
7
7
|
import Box from '@mui/material/Box';
|
|
8
|
-
import {
|
|
8
|
+
import { ReadmeContent } from '../ReadmeContent/ReadmeContent.esm.js';
|
|
9
9
|
|
|
10
|
-
const ReadmeDialog = ({ open, onClose }) => {
|
|
10
|
+
const ReadmeDialog = ({ open, onClose, ...contentProps }) => {
|
|
11
11
|
return /* @__PURE__ */ jsxs(
|
|
12
12
|
Dialog,
|
|
13
13
|
{
|
|
@@ -17,7 +17,7 @@ const ReadmeDialog = ({ open, onClose }) => {
|
|
|
17
17
|
"data-testid": "content-dialog",
|
|
18
18
|
children: [
|
|
19
19
|
/* @__PURE__ */ jsx(DialogTitle, { children: "README" }),
|
|
20
|
-
/* @__PURE__ */ jsx(DialogContent, { dividers: true, children: /* @__PURE__ */ jsx(Box, { minWidth: 650, children: /* @__PURE__ */ jsx(
|
|
20
|
+
/* @__PURE__ */ jsx(DialogContent, { dividers: true, children: /* @__PURE__ */ jsx(Box, { minWidth: 650, children: /* @__PURE__ */ jsx(ReadmeContent, { ...contentProps }) }) }),
|
|
21
21
|
/* @__PURE__ */ jsx(DialogActions, { children: /* @__PURE__ */ jsx(
|
|
22
22
|
Button,
|
|
23
23
|
{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ReadmeDialog.esm.js","sources":["../../../src/components/ReadmeDialog/ReadmeDialog.tsx"],"sourcesContent":["import Button from '@mui/material/Button';\nimport Dialog from '@mui/material/Dialog';\nimport DialogActions from '@mui/material/DialogActions';\nimport DialogContent from '@mui/material/DialogContent';\nimport DialogTitle from '@mui/material/DialogTitle';\nimport Box from '@mui/material/Box';\nimport {
|
|
1
|
+
{"version":3,"file":"ReadmeDialog.esm.js","sources":["../../../src/components/ReadmeDialog/ReadmeDialog.tsx"],"sourcesContent":["import Button from '@mui/material/Button';\nimport Dialog from '@mui/material/Dialog';\nimport DialogActions from '@mui/material/DialogActions';\nimport DialogContent from '@mui/material/DialogContent';\nimport DialogTitle from '@mui/material/DialogTitle';\nimport Box from '@mui/material/Box';\nimport { ReadmeContent } from '../ReadmeContent';\nimport type { ReadmeContentProps } from '../ReadmeContent/ReadmeContent';\n\ntype Props = {\n open: boolean;\n onClose: () => void;\n} & ReadmeContentProps;\n\nexport const ReadmeDialog = ({ open, onClose, ...contentProps }: Props) => {\n return (\n <Dialog\n maxWidth=\"md\"\n open={open}\n onClose={onClose}\n data-testid=\"content-dialog\"\n >\n <DialogTitle>README</DialogTitle>\n <DialogContent dividers>\n <Box minWidth={650}>\n <ReadmeContent {...contentProps} />\n </Box>\n </DialogContent>\n <DialogActions>\n <Button\n variant=\"contained\"\n color=\"primary\"\n aria-label=\"close\"\n onClick={onClose}\n >\n Close\n </Button>\n </DialogActions>\n </Dialog>\n );\n};\n"],"names":[],"mappings":";;;;;;;;;AAcO,MAAM,eAAe,CAAC,EAAE,MAAM,OAAA,EAAS,GAAG,cAAa,KAAa;AACzE,EAAA,uBACE,IAAA;AAAA,IAAC,MAAA;AAAA,IAAA;AAAA,MACC,QAAA,EAAS,IAAA;AAAA,MACT,IAAA;AAAA,MACA,OAAA;AAAA,MACA,aAAA,EAAY,gBAAA;AAAA,MAEZ,QAAA,EAAA;AAAA,wBAAA,GAAA,CAAC,eAAY,QAAA,EAAA,QAAA,EAAM,CAAA;AAAA,wBACnB,GAAA,CAAC,aAAA,EAAA,EAAc,QAAA,EAAQ,IAAA,EACrB,QAAA,kBAAA,GAAA,CAAC,GAAA,EAAA,EAAI,QAAA,EAAU,GAAA,EACb,QAAA,kBAAA,GAAA,CAAC,aAAA,EAAA,EAAe,GAAG,YAAA,EAAc,GACnC,CAAA,EACF,CAAA;AAAA,4BACC,aAAA,EAAA,EACC,QAAA,kBAAA,GAAA;AAAA,UAAC,MAAA;AAAA,UAAA;AAAA,YACC,OAAA,EAAQ,WAAA;AAAA,YACR,KAAA,EAAM,SAAA;AAAA,YACN,YAAA,EAAW,OAAA;AAAA,YACX,OAAA,EAAS,OAAA;AAAA,YACV,QAAA,EAAA;AAAA;AAAA,SAED,EACF;AAAA;AAAA;AAAA,GACF;AAEJ;;;;"}
|
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
import { useLocation, useNavigate } from 'react-router-dom';
|
|
2
|
-
import {
|
|
2
|
+
import { useCallback } from 'react';
|
|
3
3
|
|
|
4
4
|
const useFullViewParam = () => {
|
|
5
5
|
const location = useLocation();
|
|
6
6
|
const navigate = useNavigate();
|
|
7
|
-
const isFullView =
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
params.
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
|
|
7
|
+
const isFullView = new URLSearchParams(location.search).get("fullView") === "true";
|
|
8
|
+
const setFullView = useCallback(
|
|
9
|
+
(open) => {
|
|
10
|
+
const params = new URLSearchParams(location.search);
|
|
11
|
+
if (open) {
|
|
12
|
+
params.set("fullView", "true");
|
|
13
|
+
} else {
|
|
14
|
+
params.delete("fullView");
|
|
15
|
+
}
|
|
16
|
+
navigate({ search: params.toString() }, { replace: true });
|
|
17
|
+
},
|
|
18
|
+
[location.search, navigate]
|
|
19
|
+
);
|
|
20
20
|
return [isFullView, setFullView];
|
|
21
21
|
};
|
|
22
22
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useFullViewParam.esm.js","sources":["../../src/hooks/useFullViewParam.ts"],"sourcesContent":["import { useLocation, useNavigate } from 'react-router-dom';\nimport {
|
|
1
|
+
{"version":3,"file":"useFullViewParam.esm.js","sources":["../../src/hooks/useFullViewParam.ts"],"sourcesContent":["import { useLocation, useNavigate } from 'react-router-dom';\nimport { useCallback } from 'react';\n\nexport const useFullViewParam = (): [boolean, (open: boolean) => void] => {\n const location = useLocation();\n const navigate = useNavigate();\n\n const isFullView =\n new URLSearchParams(location.search).get('fullView') === 'true';\n\n const setFullView = useCallback(\n (open: boolean) => {\n const params = new URLSearchParams(location.search);\n if (open) {\n params.set('fullView', 'true');\n } else {\n params.delete('fullView');\n }\n navigate({ search: params.toString() }, { replace: true });\n },\n [location.search, navigate],\n );\n\n return [isFullView, setFullView];\n};\n"],"names":[],"mappings":";;;AAGO,MAAM,mBAAmB,MAA0C;AACxE,EAAA,MAAM,WAAW,WAAA,EAAY;AAC7B,EAAA,MAAM,WAAW,WAAA,EAAY;AAE7B,EAAA,MAAM,UAAA,GACJ,IAAI,eAAA,CAAgB,QAAA,CAAS,MAAM,CAAA,CAAE,GAAA,CAAI,UAAU,CAAA,KAAM,MAAA;AAE3D,EAAA,MAAM,WAAA,GAAc,WAAA;AAAA,IAClB,CAAC,IAAA,KAAkB;AACjB,MAAA,MAAM,MAAA,GAAS,IAAI,eAAA,CAAgB,QAAA,CAAS,MAAM,CAAA;AAClD,MAAA,IAAI,IAAA,EAAM;AACR,QAAA,MAAA,CAAO,GAAA,CAAI,YAAY,MAAM,CAAA;AAAA,MAC/B,CAAA,MAAO;AACL,QAAA,MAAA,CAAO,OAAO,UAAU,CAAA;AAAA,MAC1B;AACA,MAAA,QAAA,CAAS,EAAE,QAAQ,MAAA,CAAO,QAAA,IAAW,EAAG,EAAE,OAAA,EAAS,IAAA,EAAM,CAAA;AAAA,IAC3D,CAAA;AAAA,IACA,CAAC,QAAA,CAAS,MAAA,EAAQ,QAAQ;AAAA,GAC5B;AAEA,EAAA,OAAO,CAAC,YAAY,WAAW,CAAA;AACjC;;;;"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { useApi } from '@backstage/core-plugin-api';
|
|
2
|
+
import { useEntity } from '@backstage/plugin-catalog-react';
|
|
3
|
+
import { getEntitySourceLocation, stringifyEntityRef } from '@backstage/catalog-model';
|
|
4
|
+
import useAsync from 'react-use/lib/useAsync';
|
|
5
|
+
import { readmeApiRef } from '../api/ReadmeApi.esm.js';
|
|
6
|
+
|
|
7
|
+
const useReadmeContent = () => {
|
|
8
|
+
const { entity } = useEntity();
|
|
9
|
+
const readmeApi = useApi(readmeApiRef);
|
|
10
|
+
let location;
|
|
11
|
+
try {
|
|
12
|
+
const { target } = getEntitySourceLocation(entity);
|
|
13
|
+
location = target;
|
|
14
|
+
} catch {
|
|
15
|
+
location = ": Unknown";
|
|
16
|
+
}
|
|
17
|
+
const {
|
|
18
|
+
value: content,
|
|
19
|
+
loading,
|
|
20
|
+
error
|
|
21
|
+
} = useAsync(
|
|
22
|
+
async () => await readmeApi.getReadmeContent(stringifyEntityRef(entity)),
|
|
23
|
+
[entity]
|
|
24
|
+
);
|
|
25
|
+
return { content, loading, error, location };
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export { useReadmeContent };
|
|
29
|
+
//# sourceMappingURL=useReadmeContent.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useReadmeContent.esm.js","sources":["../../src/hooks/useReadmeContent.ts"],"sourcesContent":["import { useApi } from '@backstage/core-plugin-api';\nimport { useEntity } from '@backstage/plugin-catalog-react';\nimport {\n stringifyEntityRef,\n getEntitySourceLocation,\n} from '@backstage/catalog-model';\nimport useAsync from 'react-use/lib/useAsync';\nimport { readmeApiRef } from '../api/ReadmeApi';\n\nexport type UseReadmeContentResult = {\n content: string[] | undefined;\n loading: boolean;\n error: Error | undefined;\n location: string;\n};\n\nexport const useReadmeContent = (): UseReadmeContentResult => {\n const { entity } = useEntity();\n const readmeApi = useApi(readmeApiRef);\n\n let location: string;\n try {\n const { target } = getEntitySourceLocation(entity);\n location = target;\n } catch {\n location = ': Unknown';\n }\n\n const {\n value: content,\n loading,\n error,\n } = useAsync(\n async (): Promise<string[]> =>\n await readmeApi.getReadmeContent(stringifyEntityRef(entity)),\n [entity],\n );\n\n return { content, loading, error, location };\n};\n"],"names":[],"mappings":";;;;;;AAgBO,MAAM,mBAAmB,MAA8B;AAC5D,EAAA,MAAM,EAAE,MAAA,EAAO,GAAI,SAAA,EAAU;AAC7B,EAAA,MAAM,SAAA,GAAY,OAAO,YAAY,CAAA;AAErC,EAAA,IAAI,QAAA;AACJ,EAAA,IAAI;AACF,IAAA,MAAM,EAAE,MAAA,EAAO,GAAI,uBAAA,CAAwB,MAAM,CAAA;AACjD,IAAA,QAAA,GAAW,MAAA;AAAA,EACb,CAAA,CAAA,MAAQ;AACN,IAAA,QAAA,GAAW,WAAA;AAAA,EACb;AAEA,EAAA,MAAM;AAAA,IACJ,KAAA,EAAO,OAAA;AAAA,IACP,OAAA;AAAA,IACA;AAAA,GACF,GAAI,QAAA;AAAA,IACF,YACE,MAAM,SAAA,CAAU,gBAAA,CAAiB,kBAAA,CAAmB,MAAM,CAAC,CAAA;AAAA,IAC7D,CAAC,MAAM;AAAA,GACT;AAEA,EAAA,OAAO,EAAE,OAAA,EAAS,OAAA,EAAS,KAAA,EAAO,QAAA,EAAS;AAC7C;;;;"}
|
package/dist/index.d.ts
CHANGED
|
@@ -13,16 +13,27 @@ declare const readmePlugin: _backstage_core_plugin_api.BackstagePlugin<{
|
|
|
13
13
|
/**
|
|
14
14
|
* Readme card exported from the Readme plugin
|
|
15
15
|
* @public */
|
|
16
|
-
declare const ReadmeCard: (props: ReadmeCardProps) => react_jsx_runtime.JSX.Element;
|
|
16
|
+
declare const ReadmeCard: (props: ReadmeCardProps | ReadmeCardLegacyProps) => react_jsx_runtime.JSX.Element;
|
|
17
17
|
|
|
18
18
|
/**
|
|
19
|
-
*
|
|
20
|
-
*
|
|
19
|
+
* Props for the ReadmeCard component.
|
|
21
20
|
* @public
|
|
22
21
|
*/
|
|
23
22
|
type ReadmeCardProps = {
|
|
23
|
+
hideIfNotFound?: boolean;
|
|
24
|
+
maxHeight?: string | number;
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Props for the legacy MUI-based rendering of ReadmeCard.
|
|
28
|
+
* @deprecated Use {@link ReadmeCardProps} instead.
|
|
29
|
+
* @public
|
|
30
|
+
*/
|
|
31
|
+
type ReadmeCardLegacyProps = {
|
|
32
|
+
/** @deprecated Use the new {@link ReadmeCardProps} instead. */
|
|
24
33
|
variant?: InfoCardVariants;
|
|
34
|
+
/** @deprecated Use the new {@link ReadmeCardProps} instead. */
|
|
25
35
|
maxHeight?: string | number;
|
|
36
|
+
hideIfNotFound?: boolean;
|
|
26
37
|
};
|
|
27
38
|
|
|
28
39
|
/**
|
|
@@ -37,4 +48,4 @@ declare const isReadmeAvailable: (entity: Entity, context: {
|
|
|
37
48
|
apis: ApiHolder;
|
|
38
49
|
}) => Promise<boolean>;
|
|
39
50
|
|
|
40
|
-
export { ReadmeCard, type ReadmeCardProps, isReadmeAvailable, readmePlugin };
|
|
51
|
+
export { ReadmeCard, type ReadmeCardLegacyProps, type ReadmeCardProps, isReadmeAvailable, readmePlugin };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@axis-backstage/plugin-readme",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.19.0",
|
|
4
4
|
"exports": {
|
|
5
5
|
".": {
|
|
6
6
|
"import": "./dist/index.esm.js",
|
|
@@ -66,7 +66,8 @@
|
|
|
66
66
|
"@backstage/theme": "^0.7.3",
|
|
67
67
|
"@mui/icons-material": "^5.15.7",
|
|
68
68
|
"@mui/material": "^5.15.7",
|
|
69
|
-
"react-use": "^17.2.4"
|
|
69
|
+
"react-use": "^17.2.4",
|
|
70
|
+
"zod": "^4.3.6"
|
|
70
71
|
},
|
|
71
72
|
"peerDependencies": {
|
|
72
73
|
"@types/react": "^17.0.0 || ^18.0.0",
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
2
|
-
import { useEntity } from '@backstage/plugin-catalog-react';
|
|
3
|
-
import { useApi } from '@backstage/core-plugin-api';
|
|
4
|
-
import { Progress, Link, ResponseErrorPanel, ErrorPanel, MarkdownContent, CodeSnippet } from '@backstage/core-components';
|
|
5
|
-
import useAsync from 'react-use/lib/useAsync';
|
|
6
|
-
import { readmeApiRef } from '../../api/ReadmeApi.esm.js';
|
|
7
|
-
import { getEntitySourceLocation, stringifyEntityRef } from '@backstage/catalog-model';
|
|
8
|
-
import Box from '@mui/material/Box';
|
|
9
|
-
import Typography from '@mui/material/Typography';
|
|
10
|
-
import { ResponseError } from '@backstage/errors';
|
|
11
|
-
|
|
12
|
-
const FetchComponent = () => {
|
|
13
|
-
const { entity } = useEntity();
|
|
14
|
-
const readmeApi = useApi(readmeApiRef);
|
|
15
|
-
let location;
|
|
16
|
-
try {
|
|
17
|
-
const { target } = getEntitySourceLocation(entity);
|
|
18
|
-
location = target ?? target;
|
|
19
|
-
} catch (err) {
|
|
20
|
-
location = ": Unknown";
|
|
21
|
-
}
|
|
22
|
-
const {
|
|
23
|
-
value: content,
|
|
24
|
-
loading,
|
|
25
|
-
error
|
|
26
|
-
} = useAsync(
|
|
27
|
-
async () => await readmeApi.getReadmeContent(stringifyEntityRef(entity)),
|
|
28
|
-
[entity]
|
|
29
|
-
);
|
|
30
|
-
if (loading) {
|
|
31
|
-
return /* @__PURE__ */ jsx(Progress, {});
|
|
32
|
-
}
|
|
33
|
-
if (error instanceof ResponseError) {
|
|
34
|
-
if (error.statusCode === 404) {
|
|
35
|
-
return /* @__PURE__ */ jsxs(Box, { children: [
|
|
36
|
-
/* @__PURE__ */ jsxs(Typography, { pb: 2, variant: "body2", children: [
|
|
37
|
-
"No README.md file found at source location:",
|
|
38
|
-
" ",
|
|
39
|
-
location && /* @__PURE__ */ jsx("strong", { children: location })
|
|
40
|
-
] }),
|
|
41
|
-
/* @__PURE__ */ jsxs(Typography, { variant: "body2", children: [
|
|
42
|
-
"Need help? Go to our",
|
|
43
|
-
" ",
|
|
44
|
-
/* @__PURE__ */ jsx(Link, { to: "https://github.com/AxisCommunications/backstage-plugins/blob/main/plugins/readme/README.md", children: "documentation" })
|
|
45
|
-
] })
|
|
46
|
-
] });
|
|
47
|
-
}
|
|
48
|
-
return /* @__PURE__ */ jsx(ResponseErrorPanel, { error });
|
|
49
|
-
}
|
|
50
|
-
if (error) {
|
|
51
|
-
return /* @__PURE__ */ jsx(ErrorPanel, { error });
|
|
52
|
-
}
|
|
53
|
-
if (!content) {
|
|
54
|
-
return /* @__PURE__ */ jsx(ErrorPanel, { error: Error("Unknown error") });
|
|
55
|
-
}
|
|
56
|
-
if (!content[1] || content[1] === "error") {
|
|
57
|
-
return /* @__PURE__ */ jsx(ErrorPanel, { error: Error(content[1] ?? "Unknown error") });
|
|
58
|
-
} else if (content[1].startsWith("text/markdown")) {
|
|
59
|
-
return /* @__PURE__ */ jsx(MarkdownContent, { content: content[0] });
|
|
60
|
-
}
|
|
61
|
-
return /* @__PURE__ */ jsx("div", { "data-testid": "readme-content", children: /* @__PURE__ */ jsx(CodeSnippet, { text: content[0], language: "plaintext" }) });
|
|
62
|
-
};
|
|
63
|
-
|
|
64
|
-
export { FetchComponent };
|
|
65
|
-
//# sourceMappingURL=FetchComponent.esm.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"FetchComponent.esm.js","sources":["../../../src/components/FetchComponent/FetchComponent.tsx"],"sourcesContent":["import { useEntity } from '@backstage/plugin-catalog-react';\nimport { useApi } from '@backstage/core-plugin-api';\nimport {\n CodeSnippet,\n ErrorPanel,\n Link,\n MarkdownContent,\n Progress,\n ResponseErrorPanel,\n} from '@backstage/core-components';\nimport useAsync from 'react-use/lib/useAsync';\nimport { readmeApiRef } from '../../api/ReadmeApi';\nimport { stringifyEntityRef } from '@backstage/catalog-model';\nimport { getEntitySourceLocation } from '@backstage/catalog-model';\nimport Box from '@mui/material/Box';\nimport Typography from '@mui/material/Typography';\nimport { ResponseError } from '@backstage/errors';\n\nexport const FetchComponent = () => {\n const { entity } = useEntity();\n const readmeApi = useApi(readmeApiRef);\n let location;\n\n try {\n const { target } = getEntitySourceLocation(entity);\n location = target ?? target;\n } catch (err) {\n location = ': Unknown';\n }\n\n const {\n value: content,\n loading,\n error,\n } = useAsync(\n async (): Promise<string[]> =>\n await readmeApi.getReadmeContent(stringifyEntityRef(entity)),\n [entity],\n );\n\n if (loading) {\n return <Progress />;\n }\n\n if (error instanceof ResponseError) {\n if (error.statusCode === 404) {\n return (\n <Box>\n <Typography pb={2} variant=\"body2\">\n No README.md file found at source location:{' '}\n {location && <strong>{location}</strong>}\n </Typography>\n <Typography variant=\"body2\">\n Need help? Go to our{' '}\n <Link to=\"https://github.com/AxisCommunications/backstage-plugins/blob/main/plugins/readme/README.md\">\n documentation\n </Link>\n </Typography>\n </Box>\n );\n }\n return <ResponseErrorPanel error={error} />;\n }\n\n if (error) {\n return <ErrorPanel error={error} />;\n }\n if (!content) {\n return <ErrorPanel error={Error('Unknown error')} />;\n }\n if (!content[1] || content[1] === 'error') {\n return <ErrorPanel error={Error(content[1] ?? 'Unknown error')} />;\n } else if (content[1].startsWith('text/markdown')) {\n return <MarkdownContent content={content[0]} />;\n }\n // probably text/plain\n return (\n <div data-testid=\"readme-content\">\n <CodeSnippet text={content[0]} language=\"plaintext\" />\n </div>\n );\n};\n"],"names":[],"mappings":";;;;;;;;;;;AAkBO,MAAM,iBAAiB,MAAM;AAClC,EAAA,MAAM,EAAE,MAAA,EAAO,GAAI,SAAA,EAAU;AAC7B,EAAA,MAAM,SAAA,GAAY,OAAO,YAAY,CAAA;AACrC,EAAA,IAAI,QAAA;AAEJ,EAAA,IAAI;AACF,IAAA,MAAM,EAAE,MAAA,EAAO,GAAI,uBAAA,CAAwB,MAAM,CAAA;AACjD,IAAA,QAAA,GAAW,MAAA,IAAU,MAAA;AAAA,EACvB,SAAS,GAAA,EAAK;AACZ,IAAA,QAAA,GAAW,WAAA;AAAA,EACb;AAEA,EAAA,MAAM;AAAA,IACJ,KAAA,EAAO,OAAA;AAAA,IACP,OAAA;AAAA,IACA;AAAA,GACF,GAAI,QAAA;AAAA,IACF,YACE,MAAM,SAAA,CAAU,gBAAA,CAAiB,kBAAA,CAAmB,MAAM,CAAC,CAAA;AAAA,IAC7D,CAAC,MAAM;AAAA,GACT;AAEA,EAAA,IAAI,OAAA,EAAS;AACX,IAAA,2BAAQ,QAAA,EAAA,EAAS,CAAA;AAAA,EACnB;AAEA,EAAA,IAAI,iBAAiB,aAAA,EAAe;AAClC,IAAA,IAAI,KAAA,CAAM,eAAe,GAAA,EAAK;AAC5B,MAAA,4BACG,GAAA,EAAA,EACC,QAAA,EAAA;AAAA,wBAAA,IAAA,CAAC,UAAA,EAAA,EAAW,EAAA,EAAI,CAAA,EAAG,OAAA,EAAQ,OAAA,EAAQ,QAAA,EAAA;AAAA,UAAA,6CAAA;AAAA,UACW,GAAA;AAAA,UAC3C,QAAA,oBAAY,GAAA,CAAC,QAAA,EAAA,EAAQ,QAAA,EAAA,QAAA,EAAS;AAAA,SAAA,EACjC,CAAA;AAAA,wBACA,IAAA,CAAC,UAAA,EAAA,EAAW,OAAA,EAAQ,OAAA,EAAQ,QAAA,EAAA;AAAA,UAAA,sBAAA;AAAA,UACL,GAAA;AAAA,0BACrB,GAAA,CAAC,IAAA,EAAA,EAAK,EAAA,EAAG,4FAAA,EAA6F,QAAA,EAAA,eAAA,EAEtG;AAAA,SAAA,EACF;AAAA,OAAA,EACF,CAAA;AAAA,IAEJ;AACA,IAAA,uBAAO,GAAA,CAAC,sBAAmB,KAAA,EAAc,CAAA;AAAA,EAC3C;AAEA,EAAA,IAAI,KAAA,EAAO;AACT,IAAA,uBAAO,GAAA,CAAC,cAAW,KAAA,EAAc,CAAA;AAAA,EACnC;AACA,EAAA,IAAI,CAAC,OAAA,EAAS;AACZ,IAAA,uBAAO,GAAA,CAAC,UAAA,EAAA,EAAW,KAAA,EAAO,KAAA,CAAM,eAAe,CAAA,EAAG,CAAA;AAAA,EACpD;AACA,EAAA,IAAI,CAAC,OAAA,CAAQ,CAAC,KAAK,OAAA,CAAQ,CAAC,MAAM,OAAA,EAAS;AACzC,IAAA,uBAAO,GAAA,CAAC,cAAW,KAAA,EAAO,KAAA,CAAM,QAAQ,CAAC,CAAA,IAAK,eAAe,CAAA,EAAG,CAAA;AAAA,EAClE,WAAW,OAAA,CAAQ,CAAC,CAAA,CAAE,UAAA,CAAW,eAAe,CAAA,EAAG;AACjD,IAAA,uBAAO,GAAA,CAAC,eAAA,EAAA,EAAgB,OAAA,EAAS,OAAA,CAAQ,CAAC,CAAA,EAAG,CAAA;AAAA,EAC/C;AAEA,EAAA,uBACE,GAAA,CAAC,KAAA,EAAA,EAAI,aAAA,EAAY,gBAAA,EACf,QAAA,kBAAA,GAAA,CAAC,WAAA,EAAA,EAAY,IAAA,EAAM,OAAA,CAAQ,CAAC,CAAA,EAAG,QAAA,EAAS,WAAA,EAAY,CAAA,EACtD,CAAA;AAEJ;;;;"}
|