Skip to content

Commit 14752ea

Browse files
[Observability] Fetch news feed (#71212)
* adding new feed * adding log when an error happens * fixing translations Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
1 parent 1ed98d5 commit 14752ea

File tree

11 files changed

+367
-88
lines changed

11 files changed

+367
-88
lines changed

x-pack/plugins/observability/public/components/app/news/index.test.tsx

Lines changed: 0 additions & 16 deletions
This file was deleted.

x-pack/plugins/observability/public/components/app/news/mock/news.mock.data.ts

Lines changed: 0 additions & 34 deletions
This file was deleted.
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License;
4+
* you may not use this file except in compliance with the Elastic License.
5+
*/
6+
import React from 'react';
7+
import { NewsItem } from '../../../services/get_news_feed';
8+
import { render } from '../../../utils/test_helper';
9+
import { NewsFeed } from './';
10+
11+
const newsFeedItems = [
12+
{
13+
title: {
14+
en: 'Elastic introduces OpenTelemetry integration',
15+
},
16+
description: {
17+
en:
18+
'We are pleased to announce the availability of the Elastic OpenTelemetry integration — available on Elastic Cloud, or when you download Elastic APM.',
19+
},
20+
link_url: {
21+
en:
22+
'https://www.elastic.co/blog/elastic-apm-opentelemetry-integration?blade=observabilitysolutionfeed',
23+
},
24+
image_url: {
25+
en: 'foo.png',
26+
},
27+
},
28+
{
29+
title: {
30+
en: 'Kubernetes observability tutorial: Log monitoring and analysis',
31+
},
32+
description: {
33+
en:
34+
'Learn how Elastic Observability makes it easy to monitor and detect anomalies in millions of logs from thousands of containers running hundreds of microservices — while Kubernetes scales applications with changing pod counts. All from a single UI.',
35+
},
36+
link_url: {
37+
en:
38+
'https://www.elastic.co/blog/kubernetes-observability-tutorial-k8s-log-monitoring-and-analysis-elastic-stack?blade=observabilitysolutionfeed',
39+
},
40+
image_url: null,
41+
},
42+
{
43+
title: {
44+
en: 'Kubernetes observability tutorial: K8s cluster setup and demo app deployment',
45+
},
46+
description: {
47+
en:
48+
'This blog will walk you through configuring the environment you will be using for the Kubernetes observability tutorial blog series. We will be deploying Elasticsearch Service, a Minikube single-node Kubernetes cluster setup, and a demo app.',
49+
},
50+
link_url: {
51+
en:
52+
'https://www.elastic.co/blog/kubernetes-observability-tutorial-k8s-cluster-setup-demo-app-deployment?blade=observabilitysolutionfeed',
53+
},
54+
image_url: {
55+
en: null,
56+
},
57+
},
58+
] as NewsItem[];
59+
describe('News', () => {
60+
it('renders resources with all elements', () => {
61+
const { getByText, getAllByText, queryAllByTestId } = render(
62+
<NewsFeed items={newsFeedItems} />
63+
);
64+
expect(getByText("What's new")).toBeInTheDocument();
65+
expect(getAllByText('Read full story').length).toEqual(3);
66+
expect(queryAllByTestId('news_image').length).toEqual(1);
67+
});
68+
});

x-pack/plugins/observability/public/components/app/news/index.tsx renamed to x-pack/plugins/observability/public/components/app/news_feed/index.tsx

Lines changed: 42 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
* you may not use this file except in compliance with the Elastic License.
55
*/
66
import {
7+
EuiErrorBoundary,
78
EuiFlexGroup,
89
EuiFlexItem,
910
EuiHorizontalRule,
@@ -12,51 +13,51 @@ import {
1213
EuiTitle,
1314
} from '@elastic/eui';
1415
import { i18n } from '@kbn/i18n';
16+
import { truncate } from 'lodash';
1517
import React, { useContext } from 'react';
1618
import { ThemeContext } from 'styled-components';
19+
import { NewsItem as INewsItem } from '../../../services/get_news_feed';
1720
import './index.scss';
18-
import { truncate } from 'lodash';
19-
import { news as newsMockData } from './mock/news.mock.data';
2021

21-
interface NewsItem {
22-
title: string;
23-
description: string;
24-
link_url: string;
25-
image_url: string;
22+
interface Props {
23+
items: INewsItem[];
2624
}
2725

28-
export const News = () => {
29-
const newsItems: NewsItem[] = newsMockData;
26+
export const NewsFeed = ({ items }: Props) => {
3027
return (
31-
<EuiFlexGroup direction="column" gutterSize="s">
32-
<EuiFlexItem grow={false}>
33-
<EuiTitle size="xs">
34-
<h4>
35-
{i18n.translate('xpack.observability.news.title', {
36-
defaultMessage: "What's new",
37-
})}
38-
</h4>
39-
</EuiTitle>
40-
</EuiFlexItem>
41-
{newsItems.map((item, index) => (
42-
<EuiFlexItem key={index} grow={false}>
43-
<NewsItem item={item} />
28+
// The news feed is manually added/edited, to prevent any errors caused by typos or missing fields,
29+
// wraps the component with EuiErrorBoundary to avoid breaking the entire page.
30+
<EuiErrorBoundary>
31+
<EuiFlexGroup direction="column" gutterSize="s">
32+
<EuiFlexItem grow={false}>
33+
<EuiTitle size="xs">
34+
<h4>
35+
{i18n.translate('xpack.observability.news.title', {
36+
defaultMessage: "What's new",
37+
})}
38+
</h4>
39+
</EuiTitle>
4440
</EuiFlexItem>
45-
))}
46-
</EuiFlexGroup>
41+
{items.map((item, index) => (
42+
<EuiFlexItem key={index} grow={false}>
43+
<NewsItem item={item} />
44+
</EuiFlexItem>
45+
))}
46+
</EuiFlexGroup>
47+
</EuiErrorBoundary>
4748
);
4849
};
4950

5051
const limitString = (string: string, limit: number) => truncate(string, { length: limit });
5152

52-
const NewsItem = ({ item }: { item: NewsItem }) => {
53+
const NewsItem = ({ item }: { item: INewsItem }) => {
5354
const theme = useContext(ThemeContext);
5455

5556
return (
5657
<EuiFlexGroup direction="column" gutterSize="s">
5758
<EuiFlexItem grow={false}>
5859
<EuiTitle size="xxxs">
59-
<h4>{item.title}</h4>
60+
<h4>{item.title.en}</h4>
6061
</EuiTitle>
6162
</EuiFlexItem>
6263
<EuiFlexItem grow={false}>
@@ -65,11 +66,11 @@ const NewsItem = ({ item }: { item: NewsItem }) => {
6566
<EuiFlexGroup direction="column" gutterSize="s" alignItems="baseline">
6667
<EuiFlexItem>
6768
<EuiText grow={false} size="xs" color="subdued">
68-
{limitString(item.description, 128)}
69+
{limitString(item.description.en, 128)}
6970
</EuiText>
7071
</EuiFlexItem>
7172
<EuiFlexItem grow={false}>
72-
<EuiLink href={item.link_url} target="_blank">
73+
<EuiLink href={item.link_url.en} target="_blank">
7374
<EuiText size="xs">
7475
{i18n.translate('xpack.observability.news.readFullStory', {
7576
defaultMessage: 'Read full story',
@@ -79,16 +80,19 @@ const NewsItem = ({ item }: { item: NewsItem }) => {
7980
</EuiFlexItem>
8081
</EuiFlexGroup>
8182
</EuiFlexItem>
82-
<EuiFlexItem grow={false}>
83-
<img
84-
style={{ border: theme.eui.euiBorderThin }}
85-
width={48}
86-
height={48}
87-
alt={item.title}
88-
src={item.image_url}
89-
className="obsNewsFeed__itemImg"
90-
/>
91-
</EuiFlexItem>
83+
{item.image_url?.en && (
84+
<EuiFlexItem grow={false}>
85+
<img
86+
data-test-subj="news_image"
87+
style={{ border: theme.eui.euiBorderThin }}
88+
width={48}
89+
height={48}
90+
alt={item.title.en}
91+
src={item.image_url.en}
92+
className="obsNewsFeed__itemImg"
93+
/>
94+
</EuiFlexItem>
95+
)}
9296
</EuiFlexGroup>
9397
</EuiFlexItem>
9498
<EuiHorizontalRule margin="s" />

x-pack/plugins/observability/public/pages/overview/index.tsx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import { LogsSection } from '../../components/app/section/logs';
1616
import { MetricsSection } from '../../components/app/section/metrics';
1717
import { UptimeSection } from '../../components/app/section/uptime';
1818
import { DatePicker, TimePickerTime } from '../../components/shared/data_picker';
19+
import { NewsFeed } from '../../components/app/news_feed';
1920
import { fetchHasData } from '../../data_handler';
2021
import { FETCH_STATUS, useFetcher } from '../../hooks/use_fetcher';
2122
import { UI_SETTINGS, useKibanaUISettings } from '../../hooks/use_kibana_ui_settings';
@@ -26,6 +27,7 @@ import { getParsedDate } from '../../utils/date';
2627
import { getBucketSize } from '../../utils/get_bucket_size';
2728
import { getEmptySections } from './empty_section';
2829
import { LoadingObservability } from './loading_observability';
30+
import { getNewsFeed } from '../../services/get_news_feed';
2931

3032
interface Props {
3133
routeParams: RouteParams<'/overview'>;
@@ -48,6 +50,8 @@ export const OverviewPage = ({ routeParams }: Props) => {
4850
return getObservabilityAlerts({ core });
4951
}, []);
5052

53+
const { data: newsFeed } = useFetcher(() => getNewsFeed({ core }), []);
54+
5155
const theme = useContext(ThemeContext);
5256
const timePickerTime = useKibanaUISettings<TimePickerTime>(UI_SETTINGS.TIMEPICKER_TIME_DEFAULTS);
5357

@@ -190,6 +194,12 @@ export const OverviewPage = ({ routeParams }: Props) => {
190194
<EuiFlexItem grow={false}>
191195
<Resources />
192196
</EuiFlexItem>
197+
198+
{!!newsFeed?.items?.length && (
199+
<EuiFlexItem grow={false}>
200+
<NewsFeed items={newsFeed.items.slice(0, 3)} />
201+
</EuiFlexItem>
202+
)}
193203
</EuiFlexGroup>
194204
</EuiFlexItem>
195205
</EuiFlexGroup>
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License;
4+
* you may not use this file except in compliance with the Elastic License.
5+
*/
6+
7+
export const newsFeedFetchData = async () => {
8+
return {
9+
items: [
10+
{
11+
title: {
12+
en: 'Elastic introduces OpenTelemetry integration',
13+
},
14+
description: {
15+
en:
16+
'We are pleased to announce the availability of the Elastic OpenTelemetry integration — available on Elastic Cloud, or when you download Elastic APM.',
17+
},
18+
link_text: null,
19+
link_url: {
20+
en:
21+
'https://www.elastic.co/blog/elastic-apm-opentelemetry-integration?blade=observabilitysolutionfeed',
22+
},
23+
languages: null,
24+
badge: null,
25+
image_url: null,
26+
publish_on: '2020-07-02T00:00:00',
27+
expire_on: '2021-05-02T00:00:00',
28+
hash: '012caf3e161127d618ae8cc95e3e63f009a45d343eedf2f5e369cc95b1f9d9d3',
29+
},
30+
{
31+
title: {
32+
en: 'Kubernetes observability tutorial: Log monitoring and analysis',
33+
},
34+
description: {
35+
en:
36+
'Learn how Elastic Observability makes it easy to monitor and detect anomalies in millions of logs from thousands of containers running hundreds of microservices — while Kubernetes scales applications with changing pod counts. All from a single UI.',
37+
},
38+
link_text: null,
39+
link_url: {
40+
en:
41+
'https://www.elastic.co/blog/kubernetes-observability-tutorial-k8s-log-monitoring-and-analysis-elastic-stack?blade=observabilitysolutionfeed',
42+
},
43+
languages: null,
44+
badge: null,
45+
image_url: null,
46+
publish_on: '2020-06-23T00:00:00',
47+
expire_on: '2021-06-23T00:00:00',
48+
hash: '79a28cb9be717e82df80bf32c27e5d475e56d0d315be694b661d133f9a58b3b3',
49+
},
50+
{
51+
title: {
52+
en: 'Kubernetes observability tutorial: K8s cluster setup and demo app deployment',
53+
},
54+
description: {
55+
en:
56+
'This blog will walk you through configuring the environment you will be using for the Kubernetes observability tutorial blog series. We will be deploying Elasticsearch Service, a Minikube single-node Kubernetes cluster setup, and a demo app.',
57+
},
58+
link_text: null,
59+
link_url: {
60+
en:
61+
'https://www.elastic.co/blog/kubernetes-observability-tutorial-k8s-cluster-setup-demo-app-deployment?blade=observabilitysolutionfeed',
62+
},
63+
languages: null,
64+
badge: null,
65+
image_url: null,
66+
publish_on: '2020-06-23T00:00:00',
67+
expire_on: '2021-06-23T00:00:00',
68+
hash: 'ad682c355af3d4470a14df116df3b441e941661b291cdac62335615e7c6f13c2',
69+
},
70+
],
71+
};
72+
};

0 commit comments

Comments
 (0)