From f346bafdc91139140d7d7a84a11bbc32231b9fde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Tue, 11 May 2021 21:16:36 +0200 Subject: [PATCH] NewsPanel: Add support for showing social image before content (#33949) * NewsPanel: Add support for showing social image before content * Fix link and spacing * Add wide layout --- public/app/plugins/panel/news/NewsPanel.tsx | 89 ++++++++++++++------- public/app/plugins/panel/news/models.cue | 1 + public/app/plugins/panel/news/models.gen.ts | 5 +- public/app/plugins/panel/news/module.tsx | 9 +++ public/app/plugins/panel/news/rss.ts | 5 ++ public/app/plugins/panel/news/types.ts | 2 + public/app/plugins/panel/news/utils.ts | 3 + 7 files changed, 85 insertions(+), 29 deletions(-) diff --git a/public/app/plugins/panel/news/NewsPanel.tsx b/public/app/plugins/panel/news/NewsPanel.tsx index 47925667ff1..eda41bca5ca 100755 --- a/public/app/plugins/panel/news/NewsPanel.tsx +++ b/public/app/plugins/panel/news/NewsPanel.tsx @@ -9,11 +9,11 @@ import { feedToDataFrame } from './utils'; import { loadRSSFeed } from './rss'; // Types -import { PanelProps, DataFrameView, dateTimeFormat, GrafanaTheme, textUtil } from '@grafana/data'; +import { PanelProps, DataFrameView, dateTimeFormat, GrafanaTheme2, textUtil } from '@grafana/data'; import { NewsItem } from './types'; import { PanelOptions } from './models.gen'; import { DEFAULT_FEED_URL, PROXY_PREFIX } from './constants'; -import { css } from '@emotion/css'; +import { css, cx } from '@emotion/css'; interface Props extends PanelProps {} @@ -63,8 +63,11 @@ export class NewsPanel extends PureComponent { } render() { + const { width } = this.props; + const { showImage } = this.props.options; const { isError, news } = this.state; - const styles = getStyles(config.theme); + const styles = getStyles(config.theme2); + const useWideLayout = width > 600; if (isError) { return
Error Loading News
; @@ -77,17 +80,29 @@ export class NewsPanel extends PureComponent { {news.map((item, index) => { return ( -
- -
{item.title}
+
+ {showImage && item.ogImage && ( + + + + )} +
{dateTimeFormat(item.date, { format: 'MMM DD' })}
- - ); })} @@ -96,29 +111,53 @@ export class NewsPanel extends PureComponent { } } -const getStyles = stylesFactory((theme: GrafanaTheme) => ({ +const getStyles = stylesFactory((theme: GrafanaTheme2) => ({ container: css` height: 100%; `, item: css` - padding: ${theme.spacing.sm}; + display: flex; + padding: ${theme.spacing(1)}; position: relative; margin-bottom: 4px; - margin-right: ${theme.spacing.sm}; - border-bottom: 2px solid ${theme.colors.border1}; + margin-right: ${theme.spacing(1)}; + border-bottom: 2px solid ${theme.colors.border.weak}; + background: ${theme.colors.background.primary}; + flex-direction: column; + `, + itemWide: css` + flex-direction: row; + `, + body: css``, + socialImage: css` + display: flex; + align-items: center; + margin-bottom: ${theme.spacing(1)}; + > img { + width: 100%; + border-radius: ${theme.shape.borderRadius(2)} ${theme.shape.borderRadius(2)} 0 0; + } + `, + socialImageWide: css` + margin-right: ${theme.spacing(2)}; + margin-bottom: 0; + > img { + width: 250px; + border-radius: ${theme.shape.borderRadius()}; + } `, link: css` - color: ${theme.colors.linkExternal}; + color: ${theme.colors.text.link}; &:hover { - color: ${theme.colors.linkExternal}; + color: ${theme.colors.text.link}; text-decoration: underline; } `, title: css` max-width: calc(100% - 70px); font-size: 16px; - margin-bottom: ${theme.spacing.sm}; + margin-bottom: ${theme.spacing(0.5)}; `, content: css` p { @@ -127,15 +166,9 @@ const getStyles = stylesFactory((theme: GrafanaTheme) => ({ } `, date: css` - position: absolute; - top: 0; - right: 0; - background: ${theme.colors.panelBg}; - width: 55px; - text-align: right; - padding: ${theme.spacing.xs}; + margin-bottom: ${theme.spacing(0.5)}; font-weight: 500; border-radius: 0 0 0 3px; - color: ${theme.colors.textWeak}; + color: ${theme.colors.text.secondary}; `, })); diff --git a/public/app/plugins/panel/news/models.cue b/public/app/plugins/panel/news/models.cue index c9c77c8e6a7..36656dd0be7 100644 --- a/public/app/plugins/panel/news/models.cue +++ b/public/app/plugins/panel/news/models.cue @@ -8,6 +8,7 @@ Family: { // empty/missing will default to grafana blog feedUrl?: string useProxy?: bool + showImage?: bool | *true } } ] diff --git a/public/app/plugins/panel/news/models.gen.ts b/public/app/plugins/panel/news/models.gen.ts index be30b3707a3..4ef9f2dfd0e 100644 --- a/public/app/plugins/panel/news/models.gen.ts +++ b/public/app/plugins/panel/news/models.gen.ts @@ -8,6 +8,9 @@ export const modelVersion = Object.freeze([1, 0]); export interface PanelOptions { feedUrl?: string; useProxy?: boolean; + showImage?: boolean; } -export const defaultPanelOptions: PanelOptions = {}; +export const defaultPanelOptions: PanelOptions = { + showImage: true, +}; diff --git a/public/app/plugins/panel/news/module.tsx b/public/app/plugins/panel/news/module.tsx index c5e3245c54e..441f84452dc 100755 --- a/public/app/plugins/panel/news/module.tsx +++ b/public/app/plugins/panel/news/module.tsx @@ -15,6 +15,15 @@ export const plugin = new PanelPlugin(NewsPanel).setPanelOptions(( }, defaultValue: defaultPanelOptions.feedUrl, }) + .addBooleanSwitch({ + path: 'showImage', + name: 'Show image', + description: 'Controls if the news item social (og:image) image is shown above text content', + showIf: (currentConfig: PanelOptions) => { + return isString(currentConfig.feedUrl) && !currentConfig.feedUrl.startsWith(PROXY_PREFIX); + }, + defaultValue: defaultPanelOptions.showImage, + }) .addBooleanSwitch({ path: 'useProxy', name: 'Use Proxy', diff --git a/public/app/plugins/panel/news/rss.ts b/public/app/plugins/panel/news/rss.ts index 9dc99dc6d81..974548e03a4 100644 --- a/public/app/plugins/panel/news/rss.ts +++ b/public/app/plugins/panel/news/rss.ts @@ -25,6 +25,11 @@ export async function loadRSSFeed(url: string): Promise { pubDate: getProperty(node, 'pubDate'), }; + const imageNode = node.querySelector("meta[property='og:image']"); + if (imageNode) { + item.ogImage = imageNode.getAttribute('content'); + } + feed.items.push(item); }); diff --git a/public/app/plugins/panel/news/types.ts b/public/app/plugins/panel/news/types.ts index aa5d79a1d93..d87f1f8b977 100755 --- a/public/app/plugins/panel/news/types.ts +++ b/public/app/plugins/panel/news/types.ts @@ -3,6 +3,7 @@ export interface NewsItem { title: string; link: string; content: string; + ogImage?: string | null; } /** @@ -20,4 +21,5 @@ export interface RssItem { pubDate?: string; content?: string; contentSnippet?: string; + ogImage?: string | null; } diff --git a/public/app/plugins/panel/news/utils.ts b/public/app/plugins/panel/news/utils.ts index aafa1c562c8..17d6ce7cbbb 100644 --- a/public/app/plugins/panel/news/utils.ts +++ b/public/app/plugins/panel/news/utils.ts @@ -6,6 +6,7 @@ export function feedToDataFrame(feed: RssFeed): DataFrame { const title = new ArrayVector([]); const link = new ArrayVector([]); const content = new ArrayVector([]); + const ogImage = new ArrayVector([]); for (const item of feed.items) { const val = dateTime(item.pubDate); @@ -14,6 +15,7 @@ export function feedToDataFrame(feed: RssFeed): DataFrame { date.buffer.push(val.valueOf()); title.buffer.push(item.title); link.buffer.push(item.link); + ogImage.buffer.push(item.ogImage); if (item.content) { const body = item.content.replace(/<\/?[^>]+(>|$)/g, ''); @@ -30,6 +32,7 @@ export function feedToDataFrame(feed: RssFeed): DataFrame { { name: 'title', type: FieldType.string, config: {}, values: title }, { name: 'link', type: FieldType.string, config: {}, values: link }, { name: 'content', type: FieldType.string, config: {}, values: content }, + { name: 'ogImage', type: FieldType.string, config: {}, values: ogImage }, ], length: date.length, };