All files / LoadingSkeletons LoadingSkeleton.tsx

100% Statements 22/22
91.67% Branches 11/12
100% Functions 3/3
100% Lines 21/21

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134178x 178x   178x   178x 178x 178x                                                                 178x       178x                                 10x   10x 3x             7x 2x                     5x   5x     5x 23x           859x                                     5x                     5x             178x  
import React, { FunctionComponent } from 'react';
import ReactPlaceholder from 'react-placeholder';
import CSS from 'csstype';
import _ from 'lodash';
import { StandardProps } from '../../util/component-types';
import { lucidClassNames } from '../../util/style-helpers';
import LoadingIndicator from '../LoadingIndicator/LoadingIndicator';
import Panel from '../Panel/Panel';
 
export interface IStandardSkeleton extends StandardProps {
	className?: string;
	/** Displays LoadingSkeleton custom header. */
	header?: React.ReactNode;
	/** LoadingSkeleton height. */
	height?: number | string;
	/** LoadingSkeleton width. */
	width?: number | string;
}
 
export interface ILoadingSkeletonProps extends IStandardSkeleton {
	/** Allows custom skeleton to be injected. */
	Skeleton?: FunctionComponent<IStandardSkeleton>;
	/** Controls the visibility of the `LoadingSkeleton`. */
	isLoading: boolean;
	/** Children controls wrapped by skeleton. */
	children?: React.ReactNode;
 
	style?: CSS.Properties;
	/** Controls if LoadingSkeleton is wrapped in Panel. */
	isPanel?: boolean;
	/** Controls if built-in LoadingIndicator has overlay. Does not apply to other skeletons */
	hasOverlay?: boolean;
	/** Style variations for the overlay behind the loading indicator for built-in LoadingIndicator. Does not apply to other skeletons */
	overlayKind?: 'light' | 'dark';
	/** Controls if LoadingSkeleton replicated in number of rows. Default = 1. */
	numRows?: number;
	/** Controls if LoadingSkeleton replicated in number of columns. Default = 1. */
	numColumns?: number;
}
 
const animationStyle = lucidClassNames.bind(
	'&-LoadingSkeleton-animatedSkeleton'
);
 
export const LoadingSkeleton = (
	props: ILoadingSkeletonProps
): React.ReactElement => {
	const {
		Skeleton,
		isLoading,
		children,
		className,
		style,
		width = '100%',
		height = '100%',
		header,
		isPanel = false,
		hasOverlay = false,
		overlayKind,
		numRows = 1,
		numColumns = 1,
	} = { ...props };
 
	if (!isLoading) {
		return (
			<div className={className} data-test-id='loadingSkeleton-Children'>
				{children}
			</div>
		);
	}
 
	if (!Skeleton) {
		return (
			<LoadingIndicator
				data-test-id='loadingSkeleton-LoadingIndicator'
				isLoading
				isVisible
				hasOverlay={hasOverlay}
				overlayKind={overlayKind}
			/>
		);
	}
 
	const skeletonProps = { ...style, width, height, display: 'flex' };
	const skeletonPlaceholder = (
		<Skeleton data-test-id='loadingSkeleton_Skeleton' {...skeletonProps} />
	);
 
	const matrix = _.times(numColumns, column => (
		<div
			key={`column${column}`}
			style={{ display: 'inline-block' }}
			data-test-id='loadingSkeleton-SkeletonColumn'
		>
			{_.times(numRows, row => (
				<div
					className={animationStyle('&', className)}
					data-test-id='loadingSkeleton-ReactPlaceholder'
					key={`row${row}`}
					style={{ margin: 2, padding: 2 }}
				>
					<ReactPlaceholder
						showLoadingAnimation={true}
						ready={!isLoading}
						customPlaceholder={skeletonPlaceholder}
					>
						{}
					</ReactPlaceholder>
				</div>
			))}
		</div>
	));
 
	const skeleton = (
		<div style={{ marginBottom: 5, marginTop: 5 }}>
			<div
				data-test-id='loadingSkeleton-SkeletonHeader'
				style={{ marginBottom: 5, visibility: 'visible' }}
			>
				{header}
			</div>
			<div>{matrix}</div>
		</div>
	);
 
	return isPanel ? (
		<Panel data-test-id='loadingSkeleton-Panel'>{skeleton}</Panel>
	) : (
		skeleton
	);
};
 
export default React.memo(LoadingSkeleton);