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} +
+ ); +};