Merge pull request #7647 from backstage/rugvip/async
core-components: use built-in async react-syntax-highlighter component
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/core-components': patch
|
||||
---
|
||||
|
||||
Switched to relying on the built-in support for async loading in `react-syntax-highlighter`. This should provide further improvements to async rendering and lazy loading, and avoid test flakiness that was happening because of the significant number or resources being loaded in lazily all at once.
|
||||
@@ -31,6 +31,12 @@ const minProps = {
|
||||
};
|
||||
|
||||
describe('<CodeSnippet />', () => {
|
||||
// react-syntax-highlighter is large and can cause significant slowdowns
|
||||
// This test makes sure we're loading things in asynchronously and not too broadly.
|
||||
it('renders quickly', async () => {
|
||||
await renderInTestApp(<CodeSnippet text="" language="javascript" />);
|
||||
}, 1000);
|
||||
|
||||
it('renders text without exploding', async () => {
|
||||
const { getByText } = await renderInTestApp(<CodeSnippet {...minProps} />);
|
||||
expect(getByText(/"Hello"/)).toBeInTheDocument();
|
||||
|
||||
@@ -14,56 +14,13 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React, { lazy, Suspense } from 'react';
|
||||
import React from 'react';
|
||||
import { useTheme } from '@material-ui/core/styles';
|
||||
import { BackstageTheme } from '@backstage/theme';
|
||||
import { CopyTextButton } from '../CopyTextButton';
|
||||
import { Progress } from '../Progress';
|
||||
|
||||
const LazySyntaxHighlighter = lazy(async () => {
|
||||
const [{ default: SyntaxHighlighter }, { docco, dark }] = await Promise.all([
|
||||
import('react-syntax-highlighter'),
|
||||
import('react-syntax-highlighter/dist/cjs/styles/hljs'),
|
||||
]);
|
||||
|
||||
function LazyHighlighter(props: CodeSnippetProps) {
|
||||
const {
|
||||
text,
|
||||
language,
|
||||
showLineNumbers = false,
|
||||
highlightedNumbers,
|
||||
customStyle,
|
||||
} = props;
|
||||
const theme = useTheme<BackstageTheme>();
|
||||
const mode = theme.palette.type === 'dark' ? dark : docco;
|
||||
const highlightColor =
|
||||
theme.palette.type === 'dark' ? '#256bf3' : '#e6ffed';
|
||||
|
||||
return (
|
||||
<SyntaxHighlighter
|
||||
customStyle={customStyle}
|
||||
language={language}
|
||||
style={mode}
|
||||
showLineNumbers={showLineNumbers}
|
||||
wrapLines
|
||||
lineNumberStyle={{ color: theme.palette.textVerySubtle }}
|
||||
lineProps={(lineNumber: number) =>
|
||||
highlightedNumbers?.includes(lineNumber)
|
||||
? {
|
||||
style: {
|
||||
backgroundColor: highlightColor,
|
||||
},
|
||||
}
|
||||
: {}
|
||||
}
|
||||
>
|
||||
{text}
|
||||
</SyntaxHighlighter>
|
||||
);
|
||||
}
|
||||
|
||||
return { default: LazyHighlighter };
|
||||
});
|
||||
import { LightAsync } from 'react-syntax-highlighter';
|
||||
import dark from 'react-syntax-highlighter/dist/esm/styles/hljs/dark';
|
||||
import docco from 'react-syntax-highlighter/dist/esm/styles/hljs/docco';
|
||||
|
||||
/**
|
||||
* Properties for {@link CodeSnippet}
|
||||
@@ -112,12 +69,39 @@ export interface CodeSnippetProps {
|
||||
* providing consistent theming and copy code button
|
||||
*/
|
||||
export function CodeSnippet(props: CodeSnippetProps) {
|
||||
const { text, showCopyCodeButton = false } = props;
|
||||
const {
|
||||
text,
|
||||
language,
|
||||
showLineNumbers = false,
|
||||
highlightedNumbers,
|
||||
customStyle,
|
||||
showCopyCodeButton = false,
|
||||
} = props;
|
||||
const theme = useTheme<BackstageTheme>();
|
||||
const mode = theme.palette.type === 'dark' ? dark : docco;
|
||||
const highlightColor = theme.palette.type === 'dark' ? '#256bf3' : '#e6ffed';
|
||||
|
||||
return (
|
||||
<div style={{ position: 'relative' }}>
|
||||
<Suspense fallback={<Progress />}>
|
||||
<LazySyntaxHighlighter {...props} />
|
||||
</Suspense>
|
||||
<LightAsync
|
||||
customStyle={customStyle}
|
||||
language={language}
|
||||
style={mode}
|
||||
showLineNumbers={showLineNumbers}
|
||||
wrapLines
|
||||
lineNumberStyle={{ color: theme.palette.textVerySubtle }}
|
||||
lineProps={(lineNumber: number) =>
|
||||
highlightedNumbers?.includes(lineNumber)
|
||||
? {
|
||||
style: {
|
||||
backgroundColor: highlightColor,
|
||||
},
|
||||
}
|
||||
: {}
|
||||
}
|
||||
>
|
||||
{text}
|
||||
</LightAsync>
|
||||
{showCopyCodeButton && (
|
||||
<div style={{ position: 'absolute', top: 0, right: 0 }}>
|
||||
<CopyTextButton text={text} />
|
||||
|
||||
@@ -52,14 +52,14 @@ describe('<MarkdownContent />', () => {
|
||||
|
||||
it('render MarkdownContent component with CodeSnippet for code blocks', async () => {
|
||||
const rendered = await renderWithEffects(
|
||||
wrapInTestApp(<MarkdownContent content=" jest(test: string);" />),
|
||||
wrapInTestApp(
|
||||
<MarkdownContent content={'```typescript\njest(test: string);\n```'} />,
|
||||
),
|
||||
);
|
||||
const fp1 = rendered.getByText('jest', { selector: 'span' });
|
||||
const fp1 = await rendered.findByText('jest(test:', { selector: 'span' });
|
||||
expect(fp1).toBeInTheDocument();
|
||||
expect(fp1.className).toEqual('hljs-function');
|
||||
const fp2 = rendered.getByText('(test: string)', { selector: 'span' });
|
||||
const fp2 = rendered.getByText('string', { selector: 'span' });
|
||||
expect(fp2).toBeInTheDocument();
|
||||
expect(fp2.className).toEqual('hljs-function');
|
||||
expect(rendered.getByText(';', { selector: 'span' })).toBeInTheDocument();
|
||||
expect(rendered.getByText(');', { selector: 'span' })).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
+6
-6
@@ -45,7 +45,7 @@ const entities: Entity[] = [
|
||||
];
|
||||
|
||||
describe('<PreviewCatalogInfoComponent />', () => {
|
||||
it('renders without exploding', async () => {
|
||||
it('renders without exploding', () => {
|
||||
render(
|
||||
<PreviewCatalogInfoComponent
|
||||
repositoryUrl="http://my-repository/a/"
|
||||
@@ -56,14 +56,14 @@ describe('<PreviewCatalogInfoComponent />', () => {
|
||||
const repositoryUrl = screen.getByText(
|
||||
'http://my-repository/a/catalog-info.yaml',
|
||||
);
|
||||
const kindText = await screen.findByText('Kind_2');
|
||||
const kindText = screen.getByText(/Kind_2/);
|
||||
expect(repositoryUrl).toBeInTheDocument();
|
||||
expect(repositoryUrl).toBeVisible();
|
||||
expect(kindText).toBeInTheDocument();
|
||||
expect(kindText).toBeVisible();
|
||||
});
|
||||
|
||||
it('renders card with custom styles', async () => {
|
||||
it('renders card with custom styles', () => {
|
||||
const { result } = renderHook(() => useStyles());
|
||||
|
||||
render(
|
||||
@@ -77,14 +77,14 @@ describe('<PreviewCatalogInfoComponent />', () => {
|
||||
const repositoryUrl = screen.getByText(
|
||||
'http://my-repository/a/catalog-info.yaml',
|
||||
);
|
||||
const kindText = await screen.findByText('Kind_2');
|
||||
const kindText = screen.getByText(/Kind_2/);
|
||||
expect(repositoryUrl).toBeInTheDocument();
|
||||
expect(repositoryUrl).not.toBeVisible();
|
||||
expect(kindText).toBeInTheDocument();
|
||||
expect(kindText).not.toBeVisible();
|
||||
});
|
||||
|
||||
it('renders with custom styles', async () => {
|
||||
it('renders with custom styles', () => {
|
||||
const { result } = renderHook(() => useStyles());
|
||||
|
||||
render(
|
||||
@@ -98,7 +98,7 @@ describe('<PreviewCatalogInfoComponent />', () => {
|
||||
const repositoryUrl = screen.getByText(
|
||||
'http://my-repository/a/catalog-info.yaml',
|
||||
);
|
||||
const kindText = await screen.findByText('Kind_2');
|
||||
const kindText = screen.getByText(/Kind_2/);
|
||||
expect(repositoryUrl).toBeInTheDocument();
|
||||
expect(repositoryUrl).toBeVisible();
|
||||
expect(kindText).toBeInTheDocument();
|
||||
|
||||
Reference in New Issue
Block a user