Skip to content

Commit e0f9323

Browse files
authored
Code-split kibanaReact & kibanaUtils (#78140)
1 parent cb451cd commit e0f9323

10 files changed

Lines changed: 154 additions & 84 deletions

File tree

src/plugins/embeddable/public/lib/embeddables/error_embeddable.test.tsx

Lines changed: 22 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -17,43 +17,36 @@
1717
* under the License.
1818
*/
1919
import React from 'react';
20+
import { wait } from '@testing-library/dom';
21+
import { cleanup, render } from '@testing-library/react/pure';
2022
import { ErrorEmbeddable } from './error_embeddable';
2123
import { EmbeddableRoot } from './embeddable_root';
22-
import { mount } from 'enzyme';
24+
25+
afterEach(cleanup);
2326

2427
test('ErrorEmbeddable renders an embeddable', async () => {
2528
const embeddable = new ErrorEmbeddable('some error occurred', { id: '123', title: 'Error' });
26-
const component = mount(<EmbeddableRoot embeddable={embeddable} />);
27-
expect(
28-
component.getDOMNode().querySelectorAll('[data-test-subj="embeddableStackError"]').length
29-
).toBe(1);
30-
expect(
31-
component.getDOMNode().querySelectorAll('[data-test-subj="errorMessageMarkdown"]').length
32-
).toBe(1);
33-
expect(
34-
component
35-
.getDOMNode()
36-
.querySelectorAll('[data-test-subj="errorMessageMarkdown"]')[0]
37-
.innerHTML.includes('some error occurred')
38-
).toBe(true);
29+
const { getByTestId, getByText } = render(<EmbeddableRoot embeddable={embeddable} />);
30+
31+
expect(getByTestId('embeddableStackError')).toBeVisible();
32+
await wait(() => getByTestId('errorMessageMarkdown')); // wait for lazy markdown component
33+
expect(getByText(/some error occurred/i)).toBeVisible();
3934
});
4035

4136
test('ErrorEmbeddable renders an embeddable with markdown message', async () => {
4237
const error = '[some link](http://localhost:5601/takeMeThere)';
4338
const embeddable = new ErrorEmbeddable(error, { id: '123', title: 'Error' });
44-
const component = mount(<EmbeddableRoot embeddable={embeddable} />);
45-
expect(
46-
component.getDOMNode().querySelectorAll('[data-test-subj="embeddableStackError"]').length
47-
).toBe(1);
48-
expect(
49-
component.getDOMNode().querySelectorAll('[data-test-subj="errorMessageMarkdown"]').length
50-
).toBe(1);
51-
expect(
52-
component
53-
.getDOMNode()
54-
.querySelectorAll('[data-test-subj="errorMessageMarkdown"]')[0]
55-
.innerHTML.includes(
56-
'<a href="http://localhost:5601/takeMeThere" target="_blank" rel="noopener noreferrer">some link</a>'
57-
)
58-
).toBe(true);
39+
const { getByTestId, getByText } = render(<EmbeddableRoot embeddable={embeddable} />);
40+
41+
expect(getByTestId('embeddableStackError')).toBeVisible();
42+
await wait(() => getByTestId('errorMessageMarkdown')); // wait for lazy markdown component
43+
expect(getByText(/some link/i)).toMatchInlineSnapshot(`
44+
<a
45+
href="http://localhost:5601/takeMeThere"
46+
rel="noopener noreferrer"
47+
target="_blank"
48+
>
49+
some link
50+
</a>
51+
`);
5952
});

src/plugins/kibana_react/public/code_editor/code_editor.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,3 +186,7 @@ export class CodeEditor extends React.Component<Props, {}> {
186186
}
187187
};
188188
}
189+
190+
// React.lazy requires default export
191+
// eslint-disable-next-line import/no-default-export
192+
export default CodeEditor;

src/plugins/kibana_react/public/code_editor/index.tsx

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,23 @@
1717
* under the License.
1818
*/
1919
import React from 'react';
20+
import { EuiDelayRender, EuiLoadingContent } from '@elastic/eui';
2021
import { useUiSetting } from '../ui_settings';
21-
import { CodeEditor as BaseEditor, Props } from './code_editor';
22+
import type { Props } from './code_editor';
23+
24+
const LazyBaseEditor = React.lazy(() => import('./code_editor'));
25+
26+
const Fallback = () => (
27+
<EuiDelayRender>
28+
<EuiLoadingContent lines={3} />
29+
</EuiDelayRender>
30+
);
2231

2332
export const CodeEditor: React.FunctionComponent<Props> = (props) => {
2433
const darkMode = useUiSetting<boolean>('theme:darkMode');
25-
26-
return <BaseEditor {...props} useDarkTheme={darkMode} />;
34+
return (
35+
<React.Suspense fallback={<Fallback />}>
36+
<LazyBaseEditor {...props} useDarkTheme={darkMode} />
37+
</React.Suspense>
38+
);
2739
};

src/plugins/kibana_react/public/markdown/index.tsx

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,27 @@
1717
* under the License.
1818
*/
1919

20-
export { MarkdownSimple } from './markdown_simple';
21-
export { Markdown } from './markdown';
20+
import React from 'react';
21+
import { EuiLoadingContent, EuiDelayRender } from '@elastic/eui';
22+
import type { MarkdownSimpleProps } from './markdown_simple';
23+
import type { MarkdownProps } from './markdown';
24+
25+
const Fallback = () => (
26+
<EuiDelayRender>
27+
<EuiLoadingContent lines={3} />
28+
</EuiDelayRender>
29+
);
30+
31+
const LazyMarkdownSimple = React.lazy(() => import('./markdown_simple'));
32+
export const MarkdownSimple = (props: MarkdownSimpleProps) => (
33+
<React.Suspense fallback={<Fallback />}>
34+
<LazyMarkdownSimple {...props} />
35+
</React.Suspense>
36+
);
37+
38+
const LazyMarkdown = React.lazy(() => import('./markdown'));
39+
export const Markdown = (props: MarkdownProps) => (
40+
<React.Suspense fallback={<Fallback />}>
41+
<LazyMarkdown {...props} />
42+
</React.Suspense>
43+
);

src/plugins/kibana_react/public/markdown/markdown.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ export const markdownFactory = memoize(
8484
}
8585
);
8686

87-
interface MarkdownProps extends React.HTMLAttributes<HTMLDivElement> {
87+
export interface MarkdownProps extends React.HTMLAttributes<HTMLDivElement> {
8888
className?: string;
8989
markdown?: string;
9090
openLinksInNewTab?: boolean;
@@ -112,3 +112,7 @@ export class Markdown extends PureComponent<MarkdownProps> {
112112
);
113113
}
114114
}
115+
116+
// Needed for React.lazy
117+
// eslint-disable-next-line import/no-default-export
118+
export default Markdown;

src/plugins/kibana_react/public/markdown/markdown_simple.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,15 @@ const markdownRenderers = {
2424
root: Fragment,
2525
};
2626

27-
interface MarkdownSimpleProps {
27+
export interface MarkdownSimpleProps {
2828
children: string;
2929
}
3030

3131
// Render markdown string into JSX inside of a Fragment.
3232
export const MarkdownSimple = ({ children }: MarkdownSimpleProps) => (
3333
<ReactMarkdown renderers={markdownRenderers}>{children}</ReactMarkdown>
3434
);
35+
36+
// Needed for React.lazy
37+
// eslint-disable-next-line import/no-default-export
38+
export default MarkdownSimple;

src/plugins/kibana_react/public/table_list_view/table_list_view.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -556,3 +556,6 @@ class TableListView extends React.Component<TableListViewProps, TableListViewSta
556556
}
557557

558558
export { TableListView };
559+
560+
// eslint-disable-next-line import/no-default-export
561+
export default TableListView;

src/plugins/kibana_utils/public/history/redirect_when_missing.tsx

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,19 @@
2020
import React, { Fragment } from 'react';
2121
import { History } from 'history';
2222
import { i18n } from '@kbn/i18n';
23+
import { EuiLoadingSpinner } from '@elastic/eui';
2324
import ReactDOM from 'react-dom';
24-
import ReactMarkdown from 'react-markdown';
2525

2626
import { ApplicationStart, HttpStart, ToastsSetup } from 'kibana/public';
2727
import { SavedObjectNotFound } from '..';
2828

29+
const ReactMarkdown = React.lazy(() => import('react-markdown'));
30+
const ErrorRenderer = (props: { children: string }) => (
31+
<React.Suspense fallback={<EuiLoadingSpinner />}>
32+
<ReactMarkdown renderers={{ root: Fragment }} {...props} />
33+
</React.Suspense>
34+
);
35+
2936
interface Mapping {
3037
[key: string]: string | { app: string; path: string };
3138
}
@@ -96,16 +103,7 @@ export function redirectWhenMissing({
96103
defaultMessage: 'Saved object is missing',
97104
}),
98105
text: (element: HTMLElement) => {
99-
ReactDOM.render(
100-
<ReactMarkdown
101-
renderers={{
102-
root: Fragment,
103-
}}
104-
>
105-
{error.message}
106-
</ReactMarkdown>,
107-
element
108-
);
106+
ReactDOM.render(<ErrorRenderer>{error.message}</ErrorRenderer>, element);
109107
return () => ReactDOM.unmountComponentAtNode(element);
110108
},
111109
});

src/plugins/vis_type_markdown/public/markdown_vis_controller.test.tsx

Lines changed: 66 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,14 @@
1818
*/
1919

2020
import React from 'react';
21-
import { render, mount } from 'enzyme';
21+
import { wait } from '@testing-library/dom';
22+
import { render, cleanup } from '@testing-library/react/pure';
2223
import { MarkdownVisWrapper } from './markdown_vis_controller';
2324

25+
afterEach(cleanup);
26+
2427
describe('markdown vis controller', () => {
25-
it('should set html from markdown params', () => {
28+
it('should set html from markdown params', async () => {
2629
const vis = {
2730
params: {
2831
openLinksInNewTab: false,
@@ -32,13 +35,22 @@ describe('markdown vis controller', () => {
3235
},
3336
};
3437

35-
const wrapper = render(
38+
const { getByTestId, getByText } = render(
3639
<MarkdownVisWrapper visParams={vis.params} renderComplete={jest.fn()} fireEvent={jest.fn()} />
3740
);
38-
expect(wrapper.find('a').text()).toBe('markdown');
41+
42+
await wait(() => getByTestId('markdownBody'));
43+
44+
expect(getByText('markdown')).toMatchInlineSnapshot(`
45+
<a
46+
href="http://daringfireball.net/projects/markdown"
47+
>
48+
markdown
49+
</a>
50+
`);
3951
});
4052

41-
it('should not render the html', () => {
53+
it('should not render the html', async () => {
4254
const vis = {
4355
params: {
4456
openLinksInNewTab: false,
@@ -47,13 +59,20 @@ describe('markdown vis controller', () => {
4759
},
4860
};
4961

50-
const wrapper = render(
62+
const { getByTestId, getByText } = render(
5163
<MarkdownVisWrapper visParams={vis.params} renderComplete={jest.fn()} fireEvent={jest.fn()} />
5264
);
53-
expect(wrapper.text()).toBe('Testing <a>html</a>\n');
65+
66+
await wait(() => getByTestId('markdownBody'));
67+
68+
expect(getByText(/testing/i)).toMatchInlineSnapshot(`
69+
<p>
70+
Testing &lt;a&gt;html&lt;/a&gt;
71+
</p>
72+
`);
5473
});
5574

56-
it('should update the HTML when render again with changed params', () => {
75+
it('should update the HTML when render again with changed params', async () => {
5776
const vis = {
5877
params: {
5978
openLinksInNewTab: false,
@@ -62,13 +81,20 @@ describe('markdown vis controller', () => {
6281
},
6382
};
6483

65-
const wrapper = mount(
84+
const { getByTestId, getByText, rerender } = render(
6685
<MarkdownVisWrapper visParams={vis.params} renderComplete={jest.fn()} fireEvent={jest.fn()} />
6786
);
68-
expect(wrapper.text().trim()).toBe('Initial');
87+
88+
await wait(() => getByTestId('markdownBody'));
89+
90+
expect(getByText(/initial/i)).toBeInTheDocument();
91+
6992
vis.params.markdown = 'Updated';
70-
wrapper.setProps({ vis });
71-
expect(wrapper.text().trim()).toBe('Updated');
93+
rerender(
94+
<MarkdownVisWrapper visParams={vis.params} renderComplete={jest.fn()} fireEvent={jest.fn()} />
95+
);
96+
97+
expect(getByText(/Updated/i)).toBeInTheDocument();
7298
});
7399

74100
describe('renderComplete', () => {
@@ -86,56 +112,71 @@ describe('markdown vis controller', () => {
86112
renderComplete.mockClear();
87113
});
88114

89-
it('should be called on initial rendering', () => {
90-
mount(
115+
it('should be called on initial rendering', async () => {
116+
const { getByTestId } = render(
91117
<MarkdownVisWrapper
92118
visParams={vis.params}
93119
renderComplete={renderComplete}
94120
fireEvent={jest.fn()}
95121
/>
96122
);
97-
expect(renderComplete.mock.calls.length).toBe(1);
123+
124+
await wait(() => getByTestId('markdownBody'));
125+
126+
expect(renderComplete).toHaveBeenCalledTimes(1);
98127
});
99128

100-
it('should be called on successive render when params change', () => {
101-
mount(
129+
it('should be called on successive render when params change', async () => {
130+
const { getByTestId, rerender } = render(
102131
<MarkdownVisWrapper
103132
visParams={vis.params}
104133
renderComplete={renderComplete}
105134
fireEvent={jest.fn()}
106135
/>
107136
);
108-
expect(renderComplete.mock.calls.length).toBe(1);
137+
138+
await wait(() => getByTestId('markdownBody'));
139+
140+
expect(renderComplete).toHaveBeenCalledTimes(1);
141+
109142
renderComplete.mockClear();
110143
vis.params.markdown = 'changed';
111-
mount(
144+
145+
rerender(
112146
<MarkdownVisWrapper
113147
visParams={vis.params}
114148
renderComplete={renderComplete}
115149
fireEvent={jest.fn()}
116150
/>
117151
);
118-
expect(renderComplete.mock.calls.length).toBe(1);
152+
153+
expect(renderComplete).toHaveBeenCalledTimes(1);
119154
});
120155

121-
it('should be called on successive render even without data change', () => {
122-
mount(
156+
it('should be called on successive render even without data change', async () => {
157+
const { getByTestId, rerender } = render(
123158
<MarkdownVisWrapper
124159
visParams={vis.params}
125160
renderComplete={renderComplete}
126161
fireEvent={jest.fn()}
127162
/>
128163
);
129-
expect(renderComplete.mock.calls.length).toBe(1);
164+
165+
await wait(() => getByTestId('markdownBody'));
166+
167+
expect(renderComplete).toHaveBeenCalledTimes(1);
168+
130169
renderComplete.mockClear();
131-
mount(
170+
171+
rerender(
132172
<MarkdownVisWrapper
133173
visParams={vis.params}
134174
renderComplete={renderComplete}
135175
fireEvent={jest.fn()}
136176
/>
137177
);
138-
expect(renderComplete.mock.calls.length).toBe(1);
178+
179+
expect(renderComplete).toHaveBeenCalledTimes(1);
139180
});
140181
});
141182
});

0 commit comments

Comments
 (0)