Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
backstage
GitHub Repository: backstage/backstage
Path: blob/master/plugins/api-docs/src/components/ApiDefinitionDialog/ApiDefinitionDialog.tsx
11554 views
1
/*
2
* Copyright 2023 The Backstage Authors
3
*
4
* Licensed under the Apache License, Version 2.0 (the "License");
5
* you may not use this file except in compliance with the License.
6
* You may obtain a copy of the License at
7
*
8
* http://www.apache.org/licenses/LICENSE-2.0
9
*
10
* Unless required by applicable law or agreed to in writing, software
11
* distributed under the License is distributed on an "AS IS" BASIS,
12
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
* See the License for the specific language governing permissions and
14
* limitations under the License.
15
*/
16
17
import { ApiEntity } from '@backstage/catalog-model';
18
import { useApi } from '@backstage/core-plugin-api';
19
import Box from '@material-ui/core/Box';
20
import Button from '@material-ui/core/Button';
21
import Dialog from '@material-ui/core/Dialog';
22
import DialogActions from '@material-ui/core/DialogActions';
23
import DialogContent from '@material-ui/core/DialogContent';
24
import DialogTitle from '@material-ui/core/DialogTitle';
25
import Tab from '@material-ui/core/Tab';
26
import Tabs from '@material-ui/core/Tabs';
27
import Typography from '@material-ui/core/Typography';
28
import { makeStyles } from '@material-ui/core/styles';
29
import { ReactNode, useState, useEffect } from 'react';
30
import { apiDocsConfigRef } from '../../config';
31
import { PlainApiDefinitionWidget } from '../PlainApiDefinitionWidget';
32
import { useTranslationRef } from '@backstage/frontend-plugin-api';
33
import { apiDocsTranslationRef } from '../../translation';
34
35
const useStyles = makeStyles(theme => ({
36
fullHeightDialog: {
37
height: 'calc(100% - 64px)',
38
},
39
root: {
40
display: 'flex',
41
flexGrow: 1,
42
width: '100%',
43
height: '100%',
44
},
45
tabs: {
46
borderRight: `1px solid ${theme.palette.divider}`,
47
flexShrink: 0,
48
},
49
tabContents: {
50
flexGrow: 1,
51
overflowX: 'auto',
52
},
53
title: {
54
color: theme.palette.text.primary,
55
wordBreak: 'break-word',
56
fontSize: theme.typography.h3.fontSize,
57
marginBottom: 0,
58
},
59
type: {
60
textTransform: 'uppercase',
61
fontSize: 11,
62
opacity: 0.8,
63
marginBottom: theme.spacing(1),
64
color: theme.palette.text.primary,
65
},
66
}));
67
68
function TabPanel(props: {
69
children?: ReactNode;
70
index: number;
71
value: number;
72
}) {
73
const { children, value, index, ...other } = props;
74
const classes = useStyles();
75
return (
76
<div
77
role="tabpanel"
78
hidden={value !== index}
79
id={`vertical-tabpanel-${index}`}
80
aria-labelledby={`vertical-tab-${index}`}
81
className={classes.tabContents}
82
{...other}
83
>
84
{value === index && (
85
<Box pl={3} pr={3}>
86
{children}
87
</Box>
88
)}
89
</div>
90
);
91
}
92
93
function a11yProps(index: number) {
94
return {
95
id: `vertical-tab-${index}`,
96
'aria-controls': `vertical-tabpanel-${index}`,
97
};
98
}
99
100
/**
101
* A dialog that lets users inspect the API definition.
102
*
103
* @public
104
*/
105
export function ApiDefinitionDialog(props: {
106
open: boolean;
107
entity: ApiEntity;
108
onClose: () => void;
109
}) {
110
const { open, entity, onClose } = props;
111
const [activeTab, setActiveTab] = useState(0);
112
const classes = useStyles();
113
const { t } = useTranslationRef(apiDocsTranslationRef);
114
115
useEffect(() => {
116
setActiveTab(0);
117
}, [open]);
118
119
const config = useApi(apiDocsConfigRef);
120
const definitionWidget = config.getApiDefinitionWidget(entity);
121
122
let tabIndex = 0;
123
let tabPanelIndex = 0;
124
125
return (
126
<Dialog
127
fullWidth
128
maxWidth="xl"
129
open={open}
130
onClose={onClose}
131
aria-labelledby="api-definition-dialog-title"
132
PaperProps={{ className: classes.fullHeightDialog }}
133
>
134
<DialogTitle id="api-definition-dialog-title" disableTypography>
135
<Typography className={classes.type}>
136
API - {definitionWidget?.title ?? 'Raw'}
137
</Typography>
138
<Typography className={classes.title} variant="h1">
139
{entity.metadata.title ?? entity.metadata.name}
140
</Typography>
141
</DialogTitle>
142
<DialogContent dividers className={classes.root}>
143
<Tabs
144
orientation="vertical"
145
variant="scrollable"
146
value={activeTab}
147
onChange={(_, newValue) => setActiveTab(newValue)}
148
aria-label={t('apiDefinitionDialog.tabsAriaLabel')}
149
className={classes.tabs}
150
>
151
{definitionWidget ? (
152
<Tab label={definitionWidget.title} {...a11yProps(tabIndex++)} />
153
) : null}
154
<Tab label="Raw" {...a11yProps(tabIndex++)} />
155
</Tabs>
156
157
{definitionWidget ? (
158
<TabPanel value={activeTab} index={tabPanelIndex++}>
159
{definitionWidget.component(entity.spec.definition)}
160
</TabPanel>
161
) : null}
162
<TabPanel value={activeTab} index={tabPanelIndex++}>
163
<PlainApiDefinitionWidget
164
definition={entity.spec.definition}
165
language={definitionWidget?.rawLanguage ?? entity.spec.type}
166
/>
167
</TabPanel>
168
</DialogContent>
169
<DialogActions>
170
<Button onClick={onClose} color="primary">
171
{t('apiDefinitionDialog.closeButtonTitle')}
172
</Button>
173
</DialogActions>
174
</Dialog>
175
);
176
}
177
178