From 30ead91d38fbcfa60e91f9fb3cd062b5cbd6532d Mon Sep 17 00:00:00 2001 From: Ashley Harrison Date: Wed, 6 Dec 2023 16:06:30 +0000 Subject: [PATCH] Server Stats: Add skeleton loader (#79138) * add server stat skeleton loader * prevent flicker --- .betterer.results | 11 -- public/app/features/admin/ServerStats.tsx | 161 ++++++------------ public/app/features/admin/ServerStatsCard.tsx | 38 +++++ 3 files changed, 91 insertions(+), 119 deletions(-) create mode 100644 public/app/features/admin/ServerStatsCard.tsx diff --git a/.betterer.results b/.betterer.results index 96fc9868c1e..553a6b1ae58 100644 --- a/.betterer.results +++ b/.betterer.results @@ -1402,17 +1402,6 @@ exports[`better eslint`] = { "public/app/features/admin/OrgRolePicker.tsx:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"] ], - "public/app/features/admin/ServerStats.tsx:5381": [ - [0, 0, 0, "Styles should be written using objects.", "0"], - [0, 0, 0, "Styles should be written using objects.", "1"], - [0, 0, 0, "Styles should be written using objects.", "2"], - [0, 0, 0, "Styles should be written using objects.", "3"], - [0, 0, 0, "Styles should be written using objects.", "4"], - [0, 0, 0, "Styles should be written using objects.", "5"], - [0, 0, 0, "Styles should be written using objects.", "6"], - [0, 0, 0, "Styles should be written using objects.", "7"], - [0, 0, 0, "Styles should be written using objects.", "8"] - ], "public/app/features/admin/UpgradePage.tsx:5381": [ [0, 0, 0, "Styles should be written using objects.", "0"], [0, 0, 0, "Styles should be written using objects.", "1"], diff --git a/public/app/features/admin/ServerStats.tsx b/public/app/features/admin/ServerStats.tsx index 3d0f97d7cd7..d8728426600 100644 --- a/public/app/features/admin/ServerStats.tsx +++ b/public/app/features/admin/ServerStats.tsx @@ -3,17 +3,17 @@ import React, { useEffect, useState } from 'react'; import { GrafanaTheme2 } from '@grafana/data'; import { config } from '@grafana/runtime'; -import { CardContainer, LinkButton, useStyles2 } from '@grafana/ui'; +import { LinkButton, useStyles2 } from '@grafana/ui'; import { AccessControlAction } from 'app/types'; import { contextSrv } from '../../core/services/context_srv'; -import { Loader } from '../plugins/admin/components/Loader'; +import { ServerStatsCard } from './ServerStatsCard'; import { getServerStats, ServerStat } from './state/apis'; export const ServerStats = () => { const [stats, setStats] = useState(null); - const [isLoading, setIsLoading] = useState(false); + const [isLoading, setIsLoading] = useState(true); const styles = useStyles2(getStyles); const hasAccessToDataSources = contextSrv.hasPermission(AccessControlAction.DataSourcesRead); @@ -21,7 +21,6 @@ export const ServerStats = () => { useEffect(() => { if (contextSrv.hasPermission(AccessControlAction.ActionServerStatsRead)) { - setIsLoading(true); getServerStats().then((stats) => { setStats(stats); setIsLoading(false); @@ -36,18 +35,17 @@ export const ServerStats = () => { return ( <>

Instance statistics

- {isLoading ? ( -
- -
- ) : stats ? ( + {!isLoading && !stats ? ( +

No stats found.

+ ) : (
- @@ -57,8 +55,9 @@ export const ServerStats = () => { />
- @@ -67,8 +66,9 @@ export const ServerStats = () => { ) } /> - Alerts @@ -76,18 +76,19 @@ export const ServerStats = () => { } />
- { } />
- ) : ( -

No stats found.

)} ); @@ -107,88 +106,34 @@ export const ServerStats = () => { const getStyles = (theme: GrafanaTheme2) => { return { - title: css` - margin-bottom: ${theme.spacing(4)}; - `, - row: css` - display: flex; - justify-content: space-between; - width: 100%; + title: css({ + marginBottom: theme.spacing(4), + }), + row: css({ + display: 'flex', + justifyContent: 'space-between', + width: '100%', - & > div:not(:last-of-type) { - margin-right: ${theme.spacing(2)}; - } + '& > div:not(:last-of-type)': { + marginRight: theme.spacing(2), + }, - & > div { - width: 33.3%; - } - `, - doubleRow: css` - display: flex; - flex-direction: column; + '& > div': { + width: '33.3%', + }, + }), + doubleRow: css({ + display: 'flex', + flexDirection: 'column', - & > div:first-of-type { - margin-bottom: ${theme.spacing(2)}; - } - `, - - loader: css` - height: 290px; - `, - - notFound: css` - font-size: ${theme.typography.h6.fontSize}; - text-align: center; - height: 290px; - `, - }; -}; - -type StatCardProps = { - content: Array>; - footer?: JSX.Element | boolean; -}; - -const StatCard = ({ content, footer }: StatCardProps) => { - const styles = useStyles2(getCardStyles); - return ( - -
-
- {content.map((item) => { - return ( -
- {item.name} - {item.value} -
- ); - })} -
- {footer &&
{footer}
} -
-
- ); -}; - -const getCardStyles = (theme: GrafanaTheme2) => { - return { - container: css` - padding: ${theme.spacing(2)}; - `, - inner: css` - display: flex; - flex-direction: column; - width: 100%; - `, - content: css` - flex: 1 0 auto; - `, - row: css` - display: flex; - justify-content: space-between; - width: 100%; - margin-bottom: ${theme.spacing(2)}; - align-items: center; - `, + '& > div:first-of-type': { + marginBottom: theme.spacing(2), + }, + }), + notFound: css({ + fontSize: theme.typography.h6.fontSize, + textAlign: 'center', + height: '290px', + }), }; }; diff --git a/public/app/features/admin/ServerStatsCard.tsx b/public/app/features/admin/ServerStatsCard.tsx new file mode 100644 index 00000000000..6bb891720e9 --- /dev/null +++ b/public/app/features/admin/ServerStatsCard.tsx @@ -0,0 +1,38 @@ +import { css } from '@emotion/css'; +import React from 'react'; +import Skeleton from 'react-loading-skeleton'; + +import { GrafanaTheme2 } from '@grafana/data'; +import { CardContainer, Stack, useStyles2 } from '@grafana/ui'; + +export interface Props { + content: Array>; + isLoading?: boolean; + footer?: JSX.Element | boolean; +} + +export const ServerStatsCard = ({ content, footer, isLoading }: Props) => { + const styles = useStyles2(getStyles); + return ( + + {content.map((item, index) => ( + + {item.name} + {isLoading ? : {item.value}} + + ))} + {footer &&
{footer}
} +
+ ); +}; + +const getStyles = (theme: GrafanaTheme2) => { + return { + container: css({ + display: 'flex', + flexDirection: 'column', + gap: theme.spacing(2), + padding: theme.spacing(2), + }), + }; +};