diff --git a/CHANGELOG.md b/CHANGELOG.md
index c4de6571bbe..9ba282c8241 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -11,6 +11,7 @@
## [`9.5.0`](https://github.com/elastic/eui/tree/v9.5.0)
- Changed `EuiSuperDatePicker` to call `onRefresh` instead of `onTimeChanged` when user clicks "Refresh" button ([#1745](https://github.com/elastic/eui/pull/1745))
+- Added a new `EuiLoadingContent` component that displays blocks as placeholders for text. ([#1730](https://github.com/elastic/eui/pull/1730))
- Added documentation entry in `EuiPagination` for `activePage` prop. ([#1740](https://github.com/elastic/eui/pull/1740))
- Changed `EuiButton` to use "m" as it's default `size` prop ([#1742](https://github.com/elastic/eui/pull/1742))
- Adds type definitions for `EuiListGroup` and `EuiListGroupItem` ([#1737](https://github.com/elastic/eui/pull/1737))
diff --git a/src-docs/src/views/loading/loading_content.tsx b/src-docs/src/views/loading/loading_content.tsx
new file mode 100644
index 00000000000..8e451455cdd
--- /dev/null
+++ b/src-docs/src/views/loading/loading_content.tsx
@@ -0,0 +1,9 @@
+import React from 'react';
+
+import { EuiLoadingContent } from '../../../../src/components/loading';
+
+export default () => (
+
+
+
+);
diff --git a/src-docs/src/views/loading/loading_example.js b/src-docs/src/views/loading/loading_example.js
index bb5d82e043d..48052af7da6 100644
--- a/src-docs/src/views/loading/loading_example.js
+++ b/src-docs/src/views/loading/loading_example.js
@@ -7,9 +7,11 @@ import {
} from '../../components';
import {
+ EuiCode,
EuiLoadingKibana,
EuiLoadingSpinner,
EuiLoadingChart,
+ EuiLoadingContent,
} from '../../../../src/components';
import LoadingKibana from './loading_kibana';
@@ -24,6 +26,10 @@ import LoadingSpinner from './loading_spinner';
const loadingSpinnerSource = require('!!raw-loader!./loading_spinner');
const loadingSpinnerHtml = renderToHtml(LoadingSpinner);
+import LoadingContent from './loading_content';
+const loadingContentSource = require('!!raw-loader!./loading_content');
+const loadingContentHtml = renderToHtml(LoadingContent);
+
export const LoadingExample = {
title: 'Loading',
sections: [{
@@ -80,5 +86,23 @@ export const LoadingExample = {
props: { EuiLoadingSpinner },
demo: ,
snippet: ``
+ }, {
+ title: 'Text Content',
+ source: [{
+ type: GuideSectionTypes.JS,
+ code: loadingContentSource,
+ }, {
+ type: GuideSectionTypes.HTML,
+ code: loadingContentHtml,
+ }],
+ text: (
+
+ A simple loading animation for displaying placeholder text content.
+ You can pass in a number of lines between 1 and 10.
+
+ ),
+ props: { EuiLoadingContent },
+ demo: ,
+ snippet: ``
}],
};
diff --git a/src/components/index.js b/src/components/index.js
index 3f718c299bd..dbd06340133 100644
--- a/src/components/index.js
+++ b/src/components/index.js
@@ -200,6 +200,7 @@ export {
export {
EuiLoadingKibana,
EuiLoadingChart,
+ EuiLoadingContent,
EuiLoadingSpinner,
} from './loading';
diff --git a/src/components/loading/__snapshots__/loading_content.test.tsx.snap b/src/components/loading/__snapshots__/loading_content.test.tsx.snap
new file mode 100644
index 00000000000..e272883669f
--- /dev/null
+++ b/src/components/loading/__snapshots__/loading_content.test.tsx.snap
@@ -0,0 +1,486 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`EuiLoadingContent is rendered 1`] = `
+
+`;
+
+exports[`EuiLoadingContent lines 1 is rendered 1`] = `
+
+`;
+
+exports[`EuiLoadingContent lines 2 is rendered 1`] = `
+
+`;
+
+exports[`EuiLoadingContent lines 3 is rendered 1`] = `
+
+`;
+
+exports[`EuiLoadingContent lines 4 is rendered 1`] = `
+
+`;
+
+exports[`EuiLoadingContent lines 5 is rendered 1`] = `
+
+`;
+
+exports[`EuiLoadingContent lines 6 is rendered 1`] = `
+
+`;
+
+exports[`EuiLoadingContent lines 7 is rendered 1`] = `
+
+`;
+
+exports[`EuiLoadingContent lines 8 is rendered 1`] = `
+
+`;
+
+exports[`EuiLoadingContent lines 9 is rendered 1`] = `
+
+`;
+
+exports[`EuiLoadingContent lines 10 is rendered 1`] = `
+
+`;
diff --git a/src/components/loading/_index.scss b/src/components/loading/_index.scss
index 9e467086570..5b92ffeb378 100644
--- a/src/components/loading/_index.scss
+++ b/src/components/loading/_index.scss
@@ -1,4 +1,6 @@
+@import 'variables';
@import 'mixins';
@import 'loading_kibana';
@import 'loading_chart';
+@import 'loading_content';
@import 'loading_spinner';
diff --git a/src/components/loading/_loading_content.scss b/src/components/loading/_loading_content.scss
new file mode 100644
index 00000000000..fa86ab40f97
--- /dev/null
+++ b/src/components/loading/_loading_content.scss
@@ -0,0 +1,38 @@
+.euiLoadingContent__loader {
+ width: 100%;
+}
+
+.euiLoadingContent__singleLine {
+ width: 100%;
+ height: $euiSize;
+ margin-bottom: $euiSizeS;
+ border-radius: $euiBorderRadius;
+ overflow: hidden;
+
+ &:last-child:not(:only-child) {
+ width: 75%;
+ }
+}
+
+.euiLoadingContent__singleLineBackground {
+ width: 220%;
+ height: 100%;
+ background: linear-gradient(
+ to right,
+ $euiGradientStartStop 45%,
+ $euiGradientMiddle 50%,
+ $euiGradientStartStop 55%,
+ );
+ animation: euiLoadingContentGradientLoad 1.5s $euiAnimSlightResistance infinite;
+}
+
+@keyframes euiLoadingContentGradientLoad {
+ 0% {
+ transform: translateX(-53%);
+ }
+
+ 100% {
+ transform: translateX(0);
+ }
+}
+
diff --git a/src/components/loading/_variables.scss b/src/components/loading/_variables.scss
new file mode 100644
index 00000000000..2d4bd388dd9
--- /dev/null
+++ b/src/components/loading/_variables.scss
@@ -0,0 +1,2 @@
+$euiGradientStartStop: tintOrShade($euiColorLightShade, 5%, 12%);
+$euiGradientMiddle: tintOrShade($euiColorLightShade, 50%, 24%);
diff --git a/src/components/loading/index.ts b/src/components/loading/index.ts
index 011dfcb0c3a..cc4fb89ef07 100644
--- a/src/components/loading/index.ts
+++ b/src/components/loading/index.ts
@@ -1,3 +1,4 @@
export { EuiLoadingKibana } from './loading_kibana';
export { EuiLoadingChart } from './loading_chart';
+export { EuiLoadingContent } from './loading_content';
export { EuiLoadingSpinner } from './loading_spinner';
diff --git a/src/components/loading/loading_content.test.tsx b/src/components/loading/loading_content.test.tsx
new file mode 100644
index 00000000000..3683627c0a9
--- /dev/null
+++ b/src/components/loading/loading_content.test.tsx
@@ -0,0 +1,25 @@
+import React from 'react';
+import { render } from 'enzyme';
+import { requiredProps } from '../../test/required_props';
+
+import { EuiLoadingContent, LineRange } from './loading_content';
+
+const lines: LineRange[] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
+
+describe('EuiLoadingContent', () => {
+ test('is rendered', () => {
+ const component = render();
+
+ expect(component).toMatchSnapshot();
+ });
+
+ describe('lines', () => {
+ lines.forEach(line => {
+ test(`${line} is rendered`, () => {
+ const component = render();
+
+ expect(component).toMatchSnapshot();
+ });
+ });
+ });
+});
diff --git a/src/components/loading/loading_content.tsx b/src/components/loading/loading_content.tsx
new file mode 100644
index 00000000000..20b85d66fe2
--- /dev/null
+++ b/src/components/loading/loading_content.tsx
@@ -0,0 +1,29 @@
+import React, { FunctionComponent, HTMLAttributes } from 'react';
+import classNames from 'classnames';
+import { CommonProps } from '../common';
+
+export type LineRange = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10;
+
+export const EuiLoadingContent: FunctionComponent<
+ CommonProps &
+ HTMLAttributes & {
+ lines?: LineRange;
+ }
+> = ({ lines = 3, className, ...rest }) => {
+ const classes = classNames('euiLoadingContent', className);
+ const lineElements = [];
+
+ for (let i = 0; i < lines; i++) {
+ lineElements.push(
+
+ );
+ }
+
+ return (
+
+ {lineElements}
+
+ );
+};