diff --git a/.changeset/swift-turtles-provide.md b/.changeset/swift-turtles-provide.md
new file mode 100644
index 0000000000..52cf9a7c8f
--- /dev/null
+++ b/.changeset/swift-turtles-provide.md
@@ -0,0 +1,7 @@
+---
+'@backstage/plugin-api-docs': patch
+---
+
+Lazy load all API definition widgets. The widgets use libraries like
+`swagger-ui`, `graphiql`, and `@asyncapi/react-component` which are quite heavy
+weight. To improve initial load times, the widgets are only loaded once used.
diff --git a/plugins/api-docs/api-report.md b/plugins/api-docs/api-report.md
index de36857945..8ec6175a6d 100644
--- a/plugins/api-docs/api-report.md
+++ b/plugins/api-docs/api-report.md
@@ -84,11 +84,13 @@ export const ApiTypeTitle: ({
apiEntity: ApiEntity;
}) => JSX.Element;
-// Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts
+// Warning: (ae-forgotten-export) The symbol "AsyncApiDefinitionWidgetProps" needs to be exported by the entry point index.d.ts
// Warning: (ae-missing-release-tag) "AsyncApiDefinitionWidget" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
-export const AsyncApiDefinitionWidget: ({ definition }: Props_5) => JSX.Element;
+export const AsyncApiDefinitionWidget: ({
+ definition,
+}: AsyncApiDefinitionWidgetProps) => JSX.Element;
// Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts
// Warning: (ae-missing-release-tag) "ConsumedApisCard" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
@@ -100,7 +102,7 @@ export const ConsumedApisCard: ({ variant }: Props_2) => JSX.Element;
// Warning: (ae-missing-release-tag) "ConsumingComponentsCard" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
-export const ConsumingComponentsCard: ({ variant }: Props_6) => JSX.Element;
+export const ConsumingComponentsCard: ({ variant }: Props_5) => JSX.Element;
// Warning: (ae-missing-release-tag) "defaultDefinitionWidgets" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
@@ -169,11 +171,13 @@ export const EntityProvidingComponentsCard: ({
// @public (undocumented)
export const HasApisCard: ({ variant }: Props_3) => JSX.Element;
-// Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts
+// Warning: (ae-forgotten-export) The symbol "OpenApiDefinitionWidgetProps" needs to be exported by the entry point index.d.ts
// Warning: (ae-missing-release-tag) "OpenApiDefinitionWidget" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
-export const OpenApiDefinitionWidget: ({ definition }: Props_8) => JSX.Element;
+export const OpenApiDefinitionWidget: ({
+ definition,
+}: OpenApiDefinitionWidgetProps) => JSX.Element;
// Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts
// Warning: (ae-missing-release-tag) "PlainApiDefinitionWidget" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
@@ -182,7 +186,7 @@ export const OpenApiDefinitionWidget: ({ definition }: Props_8) => JSX.Element;
export const PlainApiDefinitionWidget: ({
definition,
language,
-}: Props_9) => JSX.Element;
+}: Props_7) => JSX.Element;
// Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts
// Warning: (ae-missing-release-tag) "ProvidedApisCard" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
@@ -194,5 +198,5 @@ export const ProvidedApisCard: ({ variant }: Props_4) => JSX.Element;
// Warning: (ae-missing-release-tag) "ProvidingComponentsCard" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
-export const ProvidingComponentsCard: ({ variant }: Props_7) => JSX.Element;
+export const ProvidingComponentsCard: ({ variant }: Props_6) => JSX.Element;
```
diff --git a/plugins/api-docs/src/components/AsyncApiDefinitionWidget/AsyncApiDefinitionWidget.test.tsx b/plugins/api-docs/src/components/AsyncApiDefinitionWidget/AsyncApiDefinition.test.tsx
similarity index 87%
rename from plugins/api-docs/src/components/AsyncApiDefinitionWidget/AsyncApiDefinitionWidget.test.tsx
rename to plugins/api-docs/src/components/AsyncApiDefinitionWidget/AsyncApiDefinition.test.tsx
index 0170ada180..cb89476c03 100644
--- a/plugins/api-docs/src/components/AsyncApiDefinitionWidget/AsyncApiDefinitionWidget.test.tsx
+++ b/plugins/api-docs/src/components/AsyncApiDefinitionWidget/AsyncApiDefinition.test.tsx
@@ -16,9 +16,9 @@
import { renderInTestApp } from '@backstage/test-utils';
import React from 'react';
-import { AsyncApiDefinitionWidget } from './AsyncApiDefinitionWidget';
+import { AsyncApiDefinition } from './AsyncApiDefinition';
-describe('', () => {
+describe('', () => {
it('renders asyncapi spec', async () => {
const definition = `
asyncapi: 2.0.0
@@ -40,7 +40,7 @@ components:
type: string
`;
const { getByText, getAllByText } = await renderInTestApp(
- ,
+ ,
);
expect(getByText(/Account Service/i)).toBeInTheDocument();
@@ -51,7 +51,7 @@ components:
it('renders error if definition is missing', async () => {
const { getByText } = await renderInTestApp(
- ,
+ ,
);
expect(getByText(/Error/i)).toBeInTheDocument();
expect(getByText(/Document can't be null or falsey/i)).toBeInTheDocument();
diff --git a/plugins/api-docs/src/components/AsyncApiDefinitionWidget/AsyncApiDefinition.tsx b/plugins/api-docs/src/components/AsyncApiDefinitionWidget/AsyncApiDefinition.tsx
new file mode 100644
index 0000000000..1c993f67d7
--- /dev/null
+++ b/plugins/api-docs/src/components/AsyncApiDefinitionWidget/AsyncApiDefinition.tsx
@@ -0,0 +1,145 @@
+/*
+ * Copyright 2020 The Backstage Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import AsyncApi from '@asyncapi/react-component';
+import '@asyncapi/react-component/lib/styles/fiori.css';
+import { alpha, makeStyles } from '@material-ui/core/styles';
+import React from 'react';
+
+const useStyles = makeStyles(theme => ({
+ root: {
+ '& .asyncapi': {
+ 'font-family': 'inherit',
+ background: 'none',
+ },
+ '& h2': {
+ ...theme.typography.h6,
+ },
+ '& .text-teal': {
+ color: theme.palette.primary.main,
+ },
+ '& button': {
+ ...theme.typography.button,
+ background: 'none',
+ boxSizing: 'border-box',
+ minWidth: 64,
+ borderRadius: theme.shape.borderRadius,
+ transition: theme.transitions.create(
+ ['background-color', 'box-shadow', 'border'],
+ {
+ duration: theme.transitions.duration.short,
+ },
+ ),
+ padding: '5px 15px',
+ color: theme.palette.primary.main,
+ border: `1px solid ${alpha(theme.palette.primary.main, 0.5)}`,
+ '&:hover': {
+ textDecoration: 'none',
+ '&.Mui-disabled': {
+ backgroundColor: 'transparent',
+ },
+ border: `1px solid ${theme.palette.primary.main}`,
+ backgroundColor: alpha(
+ theme.palette.primary.main,
+ theme.palette.action.hoverOpacity,
+ ),
+ // Reset on touch devices, it doesn't add specificity
+ '@media (hover: none)': {
+ backgroundColor: 'transparent',
+ },
+ },
+ '&.Mui-disabled': {
+ color: theme.palette.action.disabled,
+ },
+ },
+ '& .asyncapi__collapse-button:hover': {
+ color: theme.palette.primary.main,
+ },
+ '& button.asyncapi__toggle-button': {
+ 'min-width': 'inherit',
+ },
+ '& .asyncapi__info-list li': {
+ 'border-color': theme.palette.primary.main,
+ '&:hover': {
+ color: theme.palette.text.primary,
+ 'border-color': theme.palette.primary.main,
+ 'background-color': theme.palette.primary.main,
+ },
+ },
+ '& .asyncapi__info-list li a': {
+ color: theme.palette.primary.main,
+ '&:hover': {
+ color: theme.palette.getContrastText(theme.palette.primary.main),
+ },
+ },
+ '& .asyncapi__enum': {
+ color: theme.palette.secondary.main,
+ },
+ '& .asyncapi__info, .asyncapi__channel, .asyncapi__channels > div, .asyncapi__schema, .asyncapi__channel-operations-list .asyncapi__messages-list-item .asyncapi__message, .asyncapi__message, .asyncapi__server, .asyncapi__servers > div, .asyncapi__messages > div, .asyncapi__schemas > div':
+ {
+ 'background-color': 'inherit',
+ },
+ '& .asyncapi__channel-parameters-header, .asyncapi__channel-operations-header, .asyncapi__channel-operation-oneOf-subscribe-header, .asyncapi__channel-operation-oneOf-publish-header, .asyncapi__channel-operation-message-header, .asyncapi__message-header, .asyncapi__message-header-title, .asyncapi__message-header-title > h3, .asyncapi__bindings, .asyncapi__bindings-header, .asyncapi__bindings-header > h4':
+ {
+ 'background-color': 'inherit',
+ color: theme.palette.text.primary,
+ },
+ '& .asyncapi__additional-properties-notice': {
+ color: theme.palette.text.hint,
+ },
+ '& .asyncapi__code, .asyncapi__code-pre': {
+ background: theme.palette.background.default,
+ },
+ '& .asyncapi__schema-example-header-title': {
+ color: theme.palette.text.secondary,
+ },
+ '& .asyncapi__message-headers-header, .asyncapi__message-payload-header, .asyncapi__server-variables-header, .asyncapi__server-security-header':
+ {
+ 'background-color': 'inherit',
+ color: theme.palette.text.secondary,
+ },
+ '& .asyncapi__table-header': {
+ background: theme.palette.background.default,
+ },
+ '& .asyncapi__table-body': {
+ color: theme.palette.text.primary,
+ },
+ '& .asyncapi__server-security-flow': {
+ background: theme.palette.background.default,
+ border: 'none',
+ },
+ '& .asyncapi__server-security-flows-list a': {
+ color: theme.palette.primary.main,
+ },
+ '& .asyncapi__table-row--nested': {
+ color: theme.palette.text.secondary,
+ },
+ },
+}));
+
+type Props = {
+ definition: string;
+};
+
+export const AsyncApiDefinition = ({ definition }: Props) => {
+ const classes = useStyles();
+
+ return (
+
+ );
+};
diff --git a/plugins/api-docs/src/components/AsyncApiDefinitionWidget/AsyncApiDefinitionWidget.tsx b/plugins/api-docs/src/components/AsyncApiDefinitionWidget/AsyncApiDefinitionWidget.tsx
index f32b5d01b5..a3d9bc1f64 100644
--- a/plugins/api-docs/src/components/AsyncApiDefinitionWidget/AsyncApiDefinitionWidget.tsx
+++ b/plugins/api-docs/src/components/AsyncApiDefinitionWidget/AsyncApiDefinitionWidget.tsx
@@ -14,132 +14,27 @@
* limitations under the License.
*/
-import AsyncApi from '@asyncapi/react-component';
-import '@asyncapi/react-component/lib/styles/fiori.css';
-import { fade, makeStyles } from '@material-ui/core/styles';
-import React from 'react';
+import { Progress } from '@backstage/core-components';
+import React, { Suspense } from 'react';
-const useStyles = makeStyles(theme => ({
- root: {
- '& .asyncapi': {
- 'font-family': 'inherit',
- background: 'none',
- },
- '& h2': {
- ...theme.typography.h6,
- },
- '& .text-teal': {
- color: theme.palette.primary.main,
- },
- '& button': {
- ...theme.typography.button,
- background: 'none',
- boxSizing: 'border-box',
- minWidth: 64,
- borderRadius: theme.shape.borderRadius,
- transition: theme.transitions.create(
- ['background-color', 'box-shadow', 'border'],
- {
- duration: theme.transitions.duration.short,
- },
- ),
- padding: '5px 15px',
- color: theme.palette.primary.main,
- border: `1px solid ${fade(theme.palette.primary.main, 0.5)}`,
- '&:hover': {
- textDecoration: 'none',
- '&.Mui-disabled': {
- backgroundColor: 'transparent',
- },
- border: `1px solid ${theme.palette.primary.main}`,
- backgroundColor: fade(
- theme.palette.primary.main,
- theme.palette.action.hoverOpacity,
- ),
- // Reset on touch devices, it doesn't add specificity
- '@media (hover: none)': {
- backgroundColor: 'transparent',
- },
- },
- '&.Mui-disabled': {
- color: theme.palette.action.disabled,
- },
- },
- '& .asyncapi__collapse-button:hover': {
- color: theme.palette.primary.main,
- },
- '& button.asyncapi__toggle-button': {
- 'min-width': 'inherit',
- },
- '& .asyncapi__info-list li': {
- 'border-color': theme.palette.primary.main,
- '&:hover': {
- color: theme.palette.text.primary,
- 'border-color': theme.palette.primary.main,
- 'background-color': theme.palette.primary.main,
- },
- },
- '& .asyncapi__info-list li a': {
- color: theme.palette.primary.main,
- '&:hover': {
- color: theme.palette.getContrastText(theme.palette.primary.main),
- },
- },
- '& .asyncapi__enum': {
- color: theme.palette.secondary.main,
- },
- '& .asyncapi__info, .asyncapi__channel, .asyncapi__channels > div, .asyncapi__schema, .asyncapi__channel-operations-list .asyncapi__messages-list-item .asyncapi__message, .asyncapi__message, .asyncapi__server, .asyncapi__servers > div, .asyncapi__messages > div, .asyncapi__schemas > div':
- {
- 'background-color': 'inherit',
- },
- '& .asyncapi__channel-parameters-header, .asyncapi__channel-operations-header, .asyncapi__channel-operation-oneOf-subscribe-header, .asyncapi__channel-operation-oneOf-publish-header, .asyncapi__channel-operation-message-header, .asyncapi__message-header, .asyncapi__message-header-title, .asyncapi__message-header-title > h3, .asyncapi__bindings, .asyncapi__bindings-header, .asyncapi__bindings-header > h4':
- {
- 'background-color': 'inherit',
- color: theme.palette.text.primary,
- },
- '& .asyncapi__additional-properties-notice': {
- color: theme.palette.text.hint,
- },
- '& .asyncapi__code, .asyncapi__code-pre': {
- background: theme.palette.background.default,
- },
- '& .asyncapi__schema-example-header-title': {
- color: theme.palette.text.secondary,
- },
- '& .asyncapi__message-headers-header, .asyncapi__message-payload-header, .asyncapi__server-variables-header, .asyncapi__server-security-header':
- {
- 'background-color': 'inherit',
- color: theme.palette.text.secondary,
- },
- '& .asyncapi__table-header': {
- background: theme.palette.background.default,
- },
- '& .asyncapi__table-body': {
- color: theme.palette.text.primary,
- },
- '& .asyncapi__server-security-flow': {
- background: theme.palette.background.default,
- border: 'none',
- },
- '& .asyncapi__server-security-flows-list a': {
- color: theme.palette.primary.main,
- },
- '& .asyncapi__table-row--nested': {
- color: theme.palette.text.secondary,
- },
- },
-}));
+// The asyncapi component and related CSS has a significant size, only load it
+// if the element is actually used.
+const LazyAsyncApiDefinition = React.lazy(() =>
+ import('./AsyncApiDefinition').then(m => ({
+ default: m.AsyncApiDefinition,
+ })),
+);
-type Props = {
+export type AsyncApiDefinitionWidgetProps = {
definition: string;
};
-export const AsyncApiDefinitionWidget = ({ definition }: Props) => {
- const classes = useStyles();
-
+export const AsyncApiDefinitionWidget = ({
+ definition,
+}: AsyncApiDefinitionWidgetProps) => {
return (
-
+ }>
+
+
);
};
diff --git a/plugins/api-docs/src/components/GraphQlDefinitionWidget/GraphQlDefinitionWidget.test.tsx b/plugins/api-docs/src/components/GraphQlDefinitionWidget/GraphQlDefinition.test.tsx
similarity index 89%
rename from plugins/api-docs/src/components/GraphQlDefinitionWidget/GraphQlDefinitionWidget.test.tsx
rename to plugins/api-docs/src/components/GraphQlDefinitionWidget/GraphQlDefinition.test.tsx
index 63e5a73692..fd78fa9204 100644
--- a/plugins/api-docs/src/components/GraphQlDefinitionWidget/GraphQlDefinitionWidget.test.tsx
+++ b/plugins/api-docs/src/components/GraphQlDefinitionWidget/GraphQlDefinition.test.tsx
@@ -16,9 +16,9 @@
import { renderInTestApp } from '@backstage/test-utils';
import React from 'react';
-import { GraphQlDefinitionWidget } from './GraphQlDefinitionWidget';
+import { GraphQlDefinition } from './GraphQlDefinition';
-describe('', () => {
+describe('', () => {
it('renders graphql schema', async () => {
const definition = `
"""Hello World!"""
@@ -53,7 +53,7 @@ type Film {
};
const { getByText } = await renderInTestApp(
- ,
+ ,
);
expect(getByText(/Film/i)).toBeInTheDocument();
diff --git a/plugins/api-docs/src/components/GraphQlDefinitionWidget/GraphQlDefinition.tsx b/plugins/api-docs/src/components/GraphQlDefinitionWidget/GraphQlDefinition.tsx
new file mode 100644
index 0000000000..d3fddf70cf
--- /dev/null
+++ b/plugins/api-docs/src/components/GraphQlDefinitionWidget/GraphQlDefinition.tsx
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2020 The Backstage Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { BackstageTheme } from '@backstage/theme';
+import { makeStyles } from '@material-ui/core/styles';
+import GraphiQL from 'graphiql';
+import 'graphiql/graphiql.css';
+import { buildSchema } from 'graphql';
+import React from 'react';
+
+const useStyles = makeStyles(() => ({
+ root: {
+ height: '100%',
+ display: 'flex',
+ flexFlow: 'column nowrap',
+ },
+ graphiQlWrapper: {
+ flex: 1,
+ '@global': {
+ '.graphiql-container': {
+ boxSizing: 'initial',
+ height: '100%',
+ minHeight: '600px',
+ flex: '1 1 auto',
+ },
+ },
+ },
+}));
+
+type Props = {
+ definition: string;
+};
+
+export const GraphQlDefinition = ({ definition }: Props) => {
+ const classes = useStyles();
+ const schema = buildSchema(definition);
+
+ return (
+
+
+ Promise.resolve(null) as any}
+ schema={schema}
+ docExplorerOpen
+ defaultSecondaryEditorOpen={false}
+ />
+
+
+ );
+};
diff --git a/plugins/api-docs/src/components/GraphQlDefinitionWidget/GraphQlDefinitionWidget.tsx b/plugins/api-docs/src/components/GraphQlDefinitionWidget/GraphQlDefinitionWidget.tsx
index 8d15ba08a2..e91387bffa 100644
--- a/plugins/api-docs/src/components/GraphQlDefinitionWidget/GraphQlDefinitionWidget.tsx
+++ b/plugins/api-docs/src/components/GraphQlDefinitionWidget/GraphQlDefinitionWidget.tsx
@@ -14,54 +14,27 @@
* limitations under the License.
*/
-import { BackstageTheme } from '@backstage/theme';
-import { makeStyles } from '@material-ui/core/styles';
-import 'graphiql/graphiql.css';
-import { buildSchema } from 'graphql';
-import React, { Suspense } from 'react';
import { Progress } from '@backstage/core-components';
+import React, { Suspense } from 'react';
-const GraphiQL = React.lazy(() => import('graphiql'));
+// The graphql component, graphql and related CSS has a significant size, only
+// load it if the element is actually used.
+const LazyGraphQlDefinition = React.lazy(() =>
+ import('./GraphQlDefinition').then(m => ({
+ default: m.GraphQlDefinition,
+ })),
+);
-const useStyles = makeStyles(() => ({
- root: {
- height: '100%',
- display: 'flex',
- flexFlow: 'column nowrap',
- },
- graphiQlWrapper: {
- flex: 1,
- '@global': {
- '.graphiql-container': {
- boxSizing: 'initial',
- height: '100%',
- minHeight: '600px',
- flex: '1 1 auto',
- },
- },
- },
-}));
-
-type Props = {
- definition: any;
+export type GraphQlDefinitionWidgetProps = {
+ definition: string;
};
-export const GraphQlDefinitionWidget = ({ definition }: Props) => {
- const classes = useStyles();
- const schema = buildSchema(definition);
-
+export const GraphQlDefinitionWidget = ({
+ definition,
+}: GraphQlDefinitionWidgetProps) => {
return (
}>
-
-
- Promise.resolve(null) as any}
- schema={schema}
- docExplorerOpen
- defaultSecondaryEditorOpen={false}
- />
-
-
+
);
};
diff --git a/plugins/api-docs/src/components/OpenApiDefinitionWidget/OpenApiDefinitionWidget.test.tsx b/plugins/api-docs/src/components/OpenApiDefinitionWidget/OpenApiDefinition.test.tsx
similarity index 87%
rename from plugins/api-docs/src/components/OpenApiDefinitionWidget/OpenApiDefinitionWidget.test.tsx
rename to plugins/api-docs/src/components/OpenApiDefinitionWidget/OpenApiDefinition.test.tsx
index 080aa11a3a..735fba86f7 100644
--- a/plugins/api-docs/src/components/OpenApiDefinitionWidget/OpenApiDefinitionWidget.test.tsx
+++ b/plugins/api-docs/src/components/OpenApiDefinitionWidget/OpenApiDefinition.test.tsx
@@ -17,9 +17,9 @@
import { renderInTestApp } from '@backstage/test-utils';
import { waitFor } from '@testing-library/react';
import React from 'react';
-import { OpenApiDefinitionWidget } from './OpenApiDefinitionWidget';
+import { OpenApiDefinition } from './OpenApiDefinition';
-describe('', () => {
+describe('', () => {
it('renders openapi spec', async () => {
const definition = `
openapi: "3.0.0"
@@ -39,7 +39,7 @@ paths:
description: Success
`;
const { getByText } = await renderInTestApp(
- ,
+ ,
);
// swagger-ui loads the documentation asynchronously
@@ -51,7 +51,7 @@ paths:
it('renders error if definition is missing', async () => {
const { getByText } = await renderInTestApp(
- ,
+ ,
);
expect(getByText(/No API definition provided/i)).toBeInTheDocument();
});
diff --git a/plugins/api-docs/src/components/OpenApiDefinitionWidget/OpenApiDefinition.tsx b/plugins/api-docs/src/components/OpenApiDefinitionWidget/OpenApiDefinition.tsx
new file mode 100644
index 0000000000..cc3df40ae5
--- /dev/null
+++ b/plugins/api-docs/src/components/OpenApiDefinitionWidget/OpenApiDefinition.tsx
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2020 The Backstage Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { makeStyles } from '@material-ui/core/styles';
+import React, { useEffect, useState } from 'react';
+import SwaggerUI from 'swagger-ui-react';
+import 'swagger-ui-react/swagger-ui.css';
+
+const useStyles = makeStyles(theme => ({
+ root: {
+ '& .swagger-ui, .info h1, .info h2, .info h3, .info h4, .info h': {
+ 'font-family': 'inherit',
+ color: theme.palette.text.primary,
+ },
+ '& .scheme-container': {
+ 'background-color': theme.palette.background.default,
+ },
+ '& .opblock-tag, .opblock-tag small, table thead tr td, table thead tr th':
+ {
+ color: theme.palette.text.primary,
+ 'border-color': theme.palette.divider,
+ },
+ '& section.models, section.models.is-open h4': {
+ 'border-color': theme.palette.divider,
+ },
+ '& .opblock .opblock-summary-description, .parameter__type, table.headers td, .model-title, .model .property.primitive, section h3':
+ {
+ color: theme.palette.text.secondary,
+ },
+ '& .opblock .opblock-summary-operation-id, .opblock .opblock-summary-path, .opblock .opblock-summary-path__deprecated, .opblock .opblock-section-header h4, .parameter__name, .response-col_status, .response-col_links, .responses-inner h4, .swagger-ui .responses-inner h5, .opblock-section-header .btn, .tab li, .info li, .info p, .info table, section.models h4, .info .title, table.model tr.description, .property-row':
+ {
+ color: theme.palette.text.primary,
+ },
+ '& .opblock .opblock-section-header, .model-box, section.models .model-container':
+ {
+ background: theme.palette.background.default,
+ },
+ '& .prop-format, .parameter__in': {
+ color: theme.palette.text.disabled,
+ },
+ '& ': {
+ color: theme.palette.text.primary,
+ 'border-color': theme.palette.divider,
+ },
+ '& .opblock-description-wrapper p, .opblock-external-docs-wrapper p, .opblock-title_normal p, .response-control-media-type__accept-message, .opblock .opblock-section-header>label, .scheme-container .schemes>label, .info .base-url, .model':
+ {
+ color: theme.palette.text.hint,
+ },
+ '& .parameter__name.required:after': {
+ color: theme.palette.warning.dark,
+ },
+ '& .prop-type': {
+ color: theme.palette.primary.main,
+ },
+ },
+}));
+
+type Props = {
+ definition: string;
+};
+
+export const OpenApiDefinition = ({ definition }: Props) => {
+ const classes = useStyles();
+
+ // Due to a bug in the swagger-ui-react component, the component needs
+ // to be created without content first.
+ const [def, setDef] = useState('');
+
+ useEffect(() => {
+ const timer = setTimeout(() => setDef(definition), 0);
+ return () => clearTimeout(timer);
+ }, [definition, setDef]);
+
+ return (
+
+
+
+ );
+};
diff --git a/plugins/api-docs/src/components/OpenApiDefinitionWidget/OpenApiDefinitionWidget.tsx b/plugins/api-docs/src/components/OpenApiDefinitionWidget/OpenApiDefinitionWidget.tsx
index 4c22d29bc2..2c223243d1 100644
--- a/plugins/api-docs/src/components/OpenApiDefinitionWidget/OpenApiDefinitionWidget.tsx
+++ b/plugins/api-docs/src/components/OpenApiDefinitionWidget/OpenApiDefinitionWidget.tsx
@@ -14,81 +14,27 @@
* limitations under the License.
*/
-import { makeStyles } from '@material-ui/core/styles';
-import React, { useEffect, useState } from 'react';
-import SwaggerUI from 'swagger-ui-react';
-import 'swagger-ui-react/swagger-ui.css';
+import { Progress } from '@backstage/core-components';
+import React, { Suspense } from 'react';
-// TODO: Schemas
+// The swagger-ui component and related CSS has a significant size, only load it
+// if the element is actually used.
+const LazyOpenApiDefinition = React.lazy(() =>
+ import('./OpenApiDefinition').then(m => ({
+ default: m.OpenApiDefinition,
+ })),
+);
-const useStyles = makeStyles(theme => ({
- root: {
- '& .swagger-ui, .info h1, .info h2, .info h3, .info h4, .info h': {
- 'font-family': 'inherit',
- color: theme.palette.text.primary,
- },
- '& .scheme-container': {
- 'background-color': theme.palette.background.default,
- },
- '& .opblock-tag, .opblock-tag small, table thead tr td, table thead tr th':
- {
- color: theme.palette.text.primary,
- 'border-color': theme.palette.divider,
- },
- '& section.models, section.models.is-open h4': {
- 'border-color': theme.palette.divider,
- },
- '& .opblock .opblock-summary-description, .parameter__type, table.headers td, .model-title, .model .property.primitive, section h3':
- {
- color: theme.palette.text.secondary,
- },
- '& .opblock .opblock-summary-operation-id, .opblock .opblock-summary-path, .opblock .opblock-summary-path__deprecated, .opblock .opblock-section-header h4, .parameter__name, .response-col_status, .response-col_links, .responses-inner h4, .swagger-ui .responses-inner h5, .opblock-section-header .btn, .tab li, .info li, .info p, .info table, section.models h4, .info .title, table.model tr.description, .property-row':
- {
- color: theme.palette.text.primary,
- },
- '& .opblock .opblock-section-header, .model-box, section.models .model-container':
- {
- background: theme.palette.background.default,
- },
- '& .prop-format, .parameter__in': {
- color: theme.palette.text.disabled,
- },
- '& ': {
- color: theme.palette.text.primary,
- 'border-color': theme.palette.divider,
- },
- '& .opblock-description-wrapper p, .opblock-external-docs-wrapper p, .opblock-title_normal p, .response-control-media-type__accept-message, .opblock .opblock-section-header>label, .scheme-container .schemes>label, .info .base-url, .model':
- {
- color: theme.palette.text.hint,
- },
- '& .parameter__name.required:after': {
- color: theme.palette.warning.dark,
- },
- '& .prop-type': {
- color: theme.palette.primary.main,
- },
- },
-}));
-
-type Props = {
+export type OpenApiDefinitionWidgetProps = {
definition: string;
};
-export const OpenApiDefinitionWidget = ({ definition }: Props) => {
- const classes = useStyles();
-
- // Due to a bug in the swagger-ui-react component, the component needs
- // to be created without content first.
- const [def, setDef] = useState('');
-
- useEffect(() => {
- const timer = setTimeout(() => setDef(definition), 0);
- return () => clearTimeout(timer);
- }, [definition, setDef]);
-
+export const OpenApiDefinitionWidget = ({
+ definition,
+}: OpenApiDefinitionWidgetProps) => {
return (
-
-
-
+ }>
+
+
);
};