News: Add loading skeleton (#79009)

* convert to emotion object syntax

* add news skeleton
pull/79307/head
Ashley Harrison 1 year ago committed by GitHub
parent 99d893fd30
commit bae9dbe418
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 12
      .betterer.results
  2. 35
      public/app/core/components/AppChrome/News/NewsWrapper.tsx
  3. 150
      public/app/plugins/panel/news/component/News.tsx

@ -6339,18 +6339,6 @@ exports[`better eslint`] = {
"public/app/plugins/panel/logs/LogsPanel.tsx:5381": [
[0, 0, 0, "Do not use any type assertions.", "0"]
],
"public/app/plugins/panel/news/component/News.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"],
[0, 0, 0, "Styles should be written using objects.", "9"]
],
"public/app/plugins/panel/nodeGraph/Edge.tsx:5381": [
[0, 0, 0, "Do not use any type assertions.", "0"]
],

@ -3,7 +3,7 @@ import React, { useEffect } from 'react';
import { useMeasure } from 'react-use';
import { GrafanaTheme2 } from '@grafana/data';
import { LoadingPlaceholder, useStyles2 } from '@grafana/ui';
import { useStyles2 } from '@grafana/ui';
import { News } from 'app/plugins/panel/news/component/News';
import { useNewsFeed } from 'app/plugins/panel/news/useNewsFeed';
@ -19,25 +19,28 @@ export function NewsWrapper({ feedUrl }: NewsWrapperProps) {
getNews();
}, [getNews]);
if (state.loading || state.error) {
return (
<div className={styles.innerWrapper}>
{state.loading && <LoadingPlaceholder text="Loading..." />}
{state.error && state.error.message}
</div>
);
}
if (!state.value) {
return null;
if (state.error) {
return <div className={styles.innerWrapper}>{state.error && state.error.message}</div>;
}
return (
<div ref={widthRef}>
{widthMeasure.width > 0 &&
state.value.map((_, index) => (
<News key={index} index={index} showImage width={widthMeasure.width} data={state.value} />
))}
{state.loading ? (
<>
<News.Skeleton showImage width={widthMeasure.width} />
<News.Skeleton showImage width={widthMeasure.width} />
<News.Skeleton showImage width={widthMeasure.width} />
<News.Skeleton showImage width={widthMeasure.width} />
<News.Skeleton showImage width={widthMeasure.width} />
</>
) : (
<>
{widthMeasure.width > 0 &&
state.value?.map((_, index) => (
<News key={index} index={index} showImage width={widthMeasure.width} data={state.value} />
))}
</>
)}
<div className={styles.grot}>
<a href="https://grafana.com/blog/" target="_blank" rel="noreferrer" title="Go to Grafana labs blog">
<img src="public/img/grot-news.svg" alt="Grot reading news" />

@ -1,5 +1,6 @@
import { css, cx } from '@emotion/css';
import React from 'react';
import Skeleton from 'react-loading-skeleton';
import { DataFrameView, GrafanaTheme2, textUtil, dateTimeFormat } from '@grafana/data';
import { useStyles2 } from '@grafana/ui';
@ -45,68 +46,93 @@ export function News({ width, showImage, data, index }: NewsItemProps) {
);
}
const NewsSkeleton = ({ width, showImage }: Pick<NewsItemProps, 'width' | 'showImage'>) => {
const styles = useStyles2(getStyles);
const useWideLayout = width > 600;
return (
<div className={cx(styles.item, useWideLayout && styles.itemWide)}>
{showImage && (
<Skeleton
containerClassName={cx(styles.socialImage, useWideLayout && styles.socialImageWide)}
width={useWideLayout ? '250px' : '100%'}
height={useWideLayout ? '150px' : width * 0.5}
/>
)}
<div className={styles.body}>
<Skeleton containerClassName={styles.date} width={60} />
<Skeleton containerClassName={styles.title} width={250} />
<Skeleton containerClassName={styles.content} width="100%" count={6} />
</div>
</div>
);
};
News.Skeleton = NewsSkeleton;
const getStyles = (theme: GrafanaTheme2) => ({
container: css`
height: 100%;
`,
item: css`
display: flex;
padding: ${theme.spacing(1)};
position: relative;
margin-bottom: 4px;
margin-right: ${theme.spacing(1)};
border-bottom: 2px solid ${theme.colors.border.weak};
background: ${theme.colors.background.primary};
flex-direction: column;
flex-shrink: 0;
`,
itemWide: css`
flex-direction: row;
`,
body: css`
display: flex;
flex-direction: column;
`,
socialImage: css`
display: flex;
align-items: center;
margin-bottom: ${theme.spacing(1)};
> img {
width: 100%;
border-radius: ${theme.shape.radius.default} ${theme.shape.radius.default} 0 0;
}
`,
socialImageWide: css`
margin-right: ${theme.spacing(2)};
margin-bottom: 0;
> img {
width: 250px;
border-radius: ${theme.shape.radius.default};
}
`,
link: css`
color: ${theme.colors.text.link};
display: inline-block;
container: css({
height: '100%',
}),
item: css({
display: 'flex',
padding: theme.spacing(1),
position: 'relative',
marginBottom: theme.spacing(0.5),
marginRight: theme.spacing(1),
borderBottom: `2px solid ${theme.colors.border.weak}`,
background: theme.colors.background.primary,
flexDirection: 'column',
flexShrink: 0,
}),
itemWide: css({
flexDirection: 'row',
}),
body: css({
display: 'flex',
flexDirection: 'column',
flex: 1,
}),
socialImage: css({
display: 'flex',
alignItems: 'center',
marginBottom: theme.spacing(1),
'> img': {
width: '100%',
borderRadius: `${theme.shape.radius.default} ${theme.shape.radius.default} 0 0`,
},
}),
socialImageWide: css({
marginRight: theme.spacing(2),
marginBottom: 0,
'> img': {
width: '250px',
borderRadius: theme.shape.radius.default,
},
}),
link: css({
color: theme.colors.text.link,
display: 'inline-block',
&:hover {
color: ${theme.colors.text.link};
text-decoration: underline;
}
`,
title: css`
font-size: 16px;
margin-bottom: ${theme.spacing(0.5)};
`,
content: css`
p {
margin-bottom: 4px;
color: ${theme.colors.text};
}
`,
date: css`
margin-bottom: ${theme.spacing(0.5)};
font-weight: 500;
border-radius: 0 0 0 ${theme.shape.radius.default};
color: ${theme.colors.text.secondary};
`,
'&:hover': {
color: theme.colors.text.link,
textDecoration: 'underline',
},
}),
title: css({
fontSize: '16px',
marginBottom: theme.spacing(0.5),
}),
content: css({
p: {
marginBottom: theme.spacing(0.5),
color: theme.colors.text.primary,
},
}),
date: css({
marginBottom: theme.spacing(0.5),
fontWeight: 500,
borderRadius: `0 0 0 ${theme.shape.radius.default}`,
color: theme.colors.text.secondary,
}),
});

Loading…
Cancel
Save