diff --git a/src/plugins/embeddable/public/lib/embeddables/error_embeddable.test.tsx b/src/plugins/embeddable/public/lib/embeddables/error_embeddable.test.tsx
index 715827a72c61b..17a2ac3b2a32b 100644
--- a/src/plugins/embeddable/public/lib/embeddables/error_embeddable.test.tsx
+++ b/src/plugins/embeddable/public/lib/embeddables/error_embeddable.test.tsx
@@ -17,43 +17,36 @@
* under the License.
*/
import React from 'react';
+import { wait } from '@testing-library/dom';
+import { cleanup, render } from '@testing-library/react/pure';
import { ErrorEmbeddable } from './error_embeddable';
import { EmbeddableRoot } from './embeddable_root';
-import { mount } from 'enzyme';
+
+afterEach(cleanup);
test('ErrorEmbeddable renders an embeddable', async () => {
const embeddable = new ErrorEmbeddable('some error occurred', { id: '123', title: 'Error' });
- const component = mount();
- expect(
- component.getDOMNode().querySelectorAll('[data-test-subj="embeddableStackError"]').length
- ).toBe(1);
- expect(
- component.getDOMNode().querySelectorAll('[data-test-subj="errorMessageMarkdown"]').length
- ).toBe(1);
- expect(
- component
- .getDOMNode()
- .querySelectorAll('[data-test-subj="errorMessageMarkdown"]')[0]
- .innerHTML.includes('some error occurred')
- ).toBe(true);
+ const { getByTestId, getByText } = render();
+
+ expect(getByTestId('embeddableStackError')).toBeVisible();
+ await wait(() => getByTestId('errorMessageMarkdown')); // wait for lazy markdown component
+ expect(getByText(/some error occurred/i)).toBeVisible();
});
test('ErrorEmbeddable renders an embeddable with markdown message', async () => {
const error = '[some link](http://localhost:5601/takeMeThere)';
const embeddable = new ErrorEmbeddable(error, { id: '123', title: 'Error' });
- const component = mount();
- expect(
- component.getDOMNode().querySelectorAll('[data-test-subj="embeddableStackError"]').length
- ).toBe(1);
- expect(
- component.getDOMNode().querySelectorAll('[data-test-subj="errorMessageMarkdown"]').length
- ).toBe(1);
- expect(
- component
- .getDOMNode()
- .querySelectorAll('[data-test-subj="errorMessageMarkdown"]')[0]
- .innerHTML.includes(
- 'some link'
- )
- ).toBe(true);
+ const { getByTestId, getByText } = render();
+
+ expect(getByTestId('embeddableStackError')).toBeVisible();
+ await wait(() => getByTestId('errorMessageMarkdown')); // wait for lazy markdown component
+ expect(getByText(/some link/i)).toMatchInlineSnapshot(`
+
+ some link
+
+ `);
});
diff --git a/src/plugins/kibana_react/public/code_editor/code_editor.tsx b/src/plugins/kibana_react/public/code_editor/code_editor.tsx
index f049085ccff61..85b2c327e8b21 100644
--- a/src/plugins/kibana_react/public/code_editor/code_editor.tsx
+++ b/src/plugins/kibana_react/public/code_editor/code_editor.tsx
@@ -186,3 +186,7 @@ export class CodeEditor extends React.Component {
}
};
}
+
+// React.lazy requires default export
+// eslint-disable-next-line import/no-default-export
+export default CodeEditor;
diff --git a/src/plugins/kibana_react/public/code_editor/index.tsx b/src/plugins/kibana_react/public/code_editor/index.tsx
index 0ef83811d96d3..63e3d330a1c52 100644
--- a/src/plugins/kibana_react/public/code_editor/index.tsx
+++ b/src/plugins/kibana_react/public/code_editor/index.tsx
@@ -17,11 +17,23 @@
* under the License.
*/
import React from 'react';
+import { EuiDelayRender, EuiLoadingContent } from '@elastic/eui';
import { useUiSetting } from '../ui_settings';
-import { CodeEditor as BaseEditor, Props } from './code_editor';
+import type { Props } from './code_editor';
+
+const LazyBaseEditor = React.lazy(() => import('./code_editor'));
+
+const Fallback = () => (
+
+
+
+);
export const CodeEditor: React.FunctionComponent = (props) => {
const darkMode = useUiSetting('theme:darkMode');
-
- return ;
+ return (
+ }>
+
+
+ );
};
diff --git a/src/plugins/kibana_react/public/markdown/index.tsx b/src/plugins/kibana_react/public/markdown/index.tsx
index cacf223cf33ed..4d6fd0b742ee4 100644
--- a/src/plugins/kibana_react/public/markdown/index.tsx
+++ b/src/plugins/kibana_react/public/markdown/index.tsx
@@ -17,5 +17,27 @@
* under the License.
*/
-export { MarkdownSimple } from './markdown_simple';
-export { Markdown } from './markdown';
+import React from 'react';
+import { EuiLoadingContent, EuiDelayRender } from '@elastic/eui';
+import type { MarkdownSimpleProps } from './markdown_simple';
+import type { MarkdownProps } from './markdown';
+
+const Fallback = () => (
+
+
+
+);
+
+const LazyMarkdownSimple = React.lazy(() => import('./markdown_simple'));
+export const MarkdownSimple = (props: MarkdownSimpleProps) => (
+ }>
+
+
+);
+
+const LazyMarkdown = React.lazy(() => import('./markdown'));
+export const Markdown = (props: MarkdownProps) => (
+ }>
+
+
+);
diff --git a/src/plugins/kibana_react/public/markdown/markdown.tsx b/src/plugins/kibana_react/public/markdown/markdown.tsx
index 15d1c4931e60b..8bb61bc71862e 100644
--- a/src/plugins/kibana_react/public/markdown/markdown.tsx
+++ b/src/plugins/kibana_react/public/markdown/markdown.tsx
@@ -84,7 +84,7 @@ export const markdownFactory = memoize(
}
);
-interface MarkdownProps extends React.HTMLAttributes {
+export interface MarkdownProps extends React.HTMLAttributes {
className?: string;
markdown?: string;
openLinksInNewTab?: boolean;
@@ -112,3 +112,7 @@ export class Markdown extends PureComponent {
);
}
}
+
+// Needed for React.lazy
+// eslint-disable-next-line import/no-default-export
+export default Markdown;
diff --git a/src/plugins/kibana_react/public/markdown/markdown_simple.tsx b/src/plugins/kibana_react/public/markdown/markdown_simple.tsx
index a5465fd1c6fc9..71ae4e031abca 100644
--- a/src/plugins/kibana_react/public/markdown/markdown_simple.tsx
+++ b/src/plugins/kibana_react/public/markdown/markdown_simple.tsx
@@ -24,7 +24,7 @@ const markdownRenderers = {
root: Fragment,
};
-interface MarkdownSimpleProps {
+export interface MarkdownSimpleProps {
children: string;
}
@@ -32,3 +32,7 @@ interface MarkdownSimpleProps {
export const MarkdownSimple = ({ children }: MarkdownSimpleProps) => (
{children}
);
+
+// Needed for React.lazy
+// eslint-disable-next-line import/no-default-export
+export default MarkdownSimple;
diff --git a/src/plugins/kibana_react/public/table_list_view/table_list_view.tsx b/src/plugins/kibana_react/public/table_list_view/table_list_view.tsx
index 2fa1debf51b5c..e0e295723a69d 100644
--- a/src/plugins/kibana_react/public/table_list_view/table_list_view.tsx
+++ b/src/plugins/kibana_react/public/table_list_view/table_list_view.tsx
@@ -556,3 +556,6 @@ class TableListView extends React.Component import('react-markdown'));
+const ErrorRenderer = (props: { children: string }) => (
+ }>
+
+
+);
+
interface Mapping {
[key: string]: string | { app: string; path: string };
}
@@ -96,16 +103,7 @@ export function redirectWhenMissing({
defaultMessage: 'Saved object is missing',
}),
text: (element: HTMLElement) => {
- ReactDOM.render(
-
- {error.message}
- ,
- element
- );
+ ReactDOM.render({error.message}, element);
return () => ReactDOM.unmountComponentAtNode(element);
},
});
diff --git a/src/plugins/vis_type_markdown/public/markdown_vis_controller.test.tsx b/src/plugins/vis_type_markdown/public/markdown_vis_controller.test.tsx
index ff0cc89a5d9c9..6df205b21d910 100644
--- a/src/plugins/vis_type_markdown/public/markdown_vis_controller.test.tsx
+++ b/src/plugins/vis_type_markdown/public/markdown_vis_controller.test.tsx
@@ -18,11 +18,14 @@
*/
import React from 'react';
-import { render, mount } from 'enzyme';
+import { wait } from '@testing-library/dom';
+import { render, cleanup } from '@testing-library/react/pure';
import { MarkdownVisWrapper } from './markdown_vis_controller';
+afterEach(cleanup);
+
describe('markdown vis controller', () => {
- it('should set html from markdown params', () => {
+ it('should set html from markdown params', async () => {
const vis = {
params: {
openLinksInNewTab: false,
@@ -32,13 +35,22 @@ describe('markdown vis controller', () => {
},
};
- const wrapper = render(
+ const { getByTestId, getByText } = render(
);
- expect(wrapper.find('a').text()).toBe('markdown');
+
+ await wait(() => getByTestId('markdownBody'));
+
+ expect(getByText('markdown')).toMatchInlineSnapshot(`
+
+ markdown
+
+ `);
});
- it('should not render the html', () => {
+ it('should not render the html', async () => {
const vis = {
params: {
openLinksInNewTab: false,
@@ -47,13 +59,20 @@ describe('markdown vis controller', () => {
},
};
- const wrapper = render(
+ const { getByTestId, getByText } = render(
);
- expect(wrapper.text()).toBe('Testing html\n');
+
+ await wait(() => getByTestId('markdownBody'));
+
+ expect(getByText(/testing/i)).toMatchInlineSnapshot(`
+
+ Testing <a>html</a>
+
+ `);
});
- it('should update the HTML when render again with changed params', () => {
+ it('should update the HTML when render again with changed params', async () => {
const vis = {
params: {
openLinksInNewTab: false,
@@ -62,13 +81,20 @@ describe('markdown vis controller', () => {
},
};
- const wrapper = mount(
+ const { getByTestId, getByText, rerender } = render(
);
- expect(wrapper.text().trim()).toBe('Initial');
+
+ await wait(() => getByTestId('markdownBody'));
+
+ expect(getByText(/initial/i)).toBeInTheDocument();
+
vis.params.markdown = 'Updated';
- wrapper.setProps({ vis });
- expect(wrapper.text().trim()).toBe('Updated');
+ rerender(
+
+ );
+
+ expect(getByText(/Updated/i)).toBeInTheDocument();
});
describe('renderComplete', () => {
@@ -86,56 +112,71 @@ describe('markdown vis controller', () => {
renderComplete.mockClear();
});
- it('should be called on initial rendering', () => {
- mount(
+ it('should be called on initial rendering', async () => {
+ const { getByTestId } = render(
);
- expect(renderComplete.mock.calls.length).toBe(1);
+
+ await wait(() => getByTestId('markdownBody'));
+
+ expect(renderComplete).toHaveBeenCalledTimes(1);
});
- it('should be called on successive render when params change', () => {
- mount(
+ it('should be called on successive render when params change', async () => {
+ const { getByTestId, rerender } = render(
);
- expect(renderComplete.mock.calls.length).toBe(1);
+
+ await wait(() => getByTestId('markdownBody'));
+
+ expect(renderComplete).toHaveBeenCalledTimes(1);
+
renderComplete.mockClear();
vis.params.markdown = 'changed';
- mount(
+
+ rerender(
);
- expect(renderComplete.mock.calls.length).toBe(1);
+
+ expect(renderComplete).toHaveBeenCalledTimes(1);
});
- it('should be called on successive render even without data change', () => {
- mount(
+ it('should be called on successive render even without data change', async () => {
+ const { getByTestId, rerender } = render(
);
- expect(renderComplete.mock.calls.length).toBe(1);
+
+ await wait(() => getByTestId('markdownBody'));
+
+ expect(renderComplete).toHaveBeenCalledTimes(1);
+
renderComplete.mockClear();
- mount(
+
+ rerender(
);
- expect(renderComplete.mock.calls.length).toBe(1);
+
+ expect(renderComplete).toHaveBeenCalledTimes(1);
});
});
});
diff --git a/x-pack/plugins/canvas/public/components/expression_input/__stories__/__snapshots__/expression_input.stories.storyshot b/x-pack/plugins/canvas/public/components/expression_input/__stories__/__snapshots__/expression_input.stories.storyshot
index 99d5dc3c115be..5c17eb2b68137 100644
--- a/x-pack/plugins/canvas/public/components/expression_input/__stories__/__snapshots__/expression_input.stories.storyshot
+++ b/x-pack/plugins/canvas/public/components/expression_input/__stories__/__snapshots__/expression_input.stories.storyshot
@@ -16,18 +16,7 @@ exports[`Storyshots components/ExpressionInput default 1`] = `
id="generated-id"
onBlur={[Function]}
onFocus={[Function]}
- >
-
-
-
+ />