Back

100% Statements 3/3
70% Branches 7/10
100% Functions 3/3
100% Lines 3/3

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                                                      5x                                                                                 13x                                                 4x                            
import { Badge, Card, Icon, Stack, Text } from '@wordpress/ui';
import type { ComponentProps, ReactElement, ReactNode } from 'react';
 
export type CardStatus = { label: string; intent: ComponentProps< typeof Badge >[ 'intent' ] };
 
type CardProps = {
	icon: ReactElement;
	title: string;
	description?: string;
	status?: CardStatus;
	actions?: ReactNode;
	children: ReactNode;
};
 
/**
 * A Protect dashboard card: a titled header, then rows divided by rules.
 *
 * @param props             - Component props.
 * @param props.icon        - The feature's icon.
 * @param props.title       - The feature's name.
 * @param props.description - Optional line under the title.
 * @param props.status      - Optional status badge.
 * @param props.actions     - Optional controls beside the badge.
 * @param props.children    - The card's rows.
 * @return The card.
 */
export function ProtectCard( { icon, title, description, status, actions, children }: CardProps ) {
	return (
		<Card.Root render={ <section /> } className="jp-protect-card" aria-label={ title }>
			<Card.Header
				render={ <Stack direction="row" gap="sm" align="center" justify="space-between" /> }
			>
				<Stack direction="row" gap="sm" align={ description ? 'start' : 'center' }>
					<Icon icon={ icon } size={ 24 } />
					<Stack direction="column" gap="xs">
						<Card.Title render={ <h2 className="jp-protect-card__title" /> }>{ title }</Card.Title>
						{ description && (
							<Text variant="body-sm" className="jp-protect-card__muted">
								{ description }
							</Text>
						) }
					</Stack>
				</Stack>
				<Stack className="jp-protect-card__header-end" direction="row" gap="sm" align="center">
					{ status && <Badge intent={ status.intent }>{ status.label }</Badge> }
					{ actions }
				</Stack>
			</Card.Header>
			<Card.Content>{ children }</Card.Content>
		</Card.Root>
	);
}
 
/**
 * One row of a card.
 *
 * @param props           - Component props.
 * @param props.children  - Row contents.
 * @param props.className - Extra class names.
 * @return The row.
 */
export function CardRow( {
	children,
	className = '',
}: {
	children: ReactNode;
	className?: string;
} ) {
	return (
		<Card.FullBleed className={ `jp-protect-card__row ${ className }`.trim() }>
			{ children }
		</Card.FullBleed>
	);
}
 
/**
 * A labelled figure, such as a count of blocked requests.
 *
 * @param props         - Component props.
 * @param props.label   - What the figure counts.
 * @param props.value   - The figure.
 * @param props.warning - Whether to highlight the figure as needing attention.
 * @return The stat.
 */
export function Stat( {
	label,
	value,
	warning = false,
}: {
	label: string;
	value: ReactNode;
	warning?: boolean;
} ) {
	return (
		<Stack direction="column" gap="xs">
			<Text variant="body-md">{ label }</Text>
			<Text
				variant="heading-2xl"
				className={
					warning ? 'jp-protect-card__stat-value is-warning' : 'jp-protect-card__stat-value'
				}
			>
				{ value }
			</Text>
		</Stack>
	);
}