Skip to content

Commit

Permalink
Merge pull request #1049 from ivangonzalezacuna/feat/rss-feed-customi…
Browse files Browse the repository at this point in the history
…zation

Allow more RSS Feed customization
  • Loading branch information
Xantier authored Jul 24, 2023
2 parents 1ca38a6 + 14f0e4d commit 58b77fa
Show file tree
Hide file tree
Showing 8 changed files with 73 additions and 63 deletions.
9 changes: 9 additions & 0 deletions .changeset/famous-teachers-breathe.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
'@roadiehq/backstage-plugin-home-rss': minor
---

Allow more customization for RSS Feed component and minor tweaks.

- Enable/disable paging if required
- Allow modifying the rows for the table with any custom setup
- Minor changes in the component to work better with new custom homepage
5 changes: 5 additions & 0 deletions plugins/home/backstage-plugin-home-rss/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,14 @@ export const HomePage = () => {
<HomePageRSS
feedURL="http://localhost:7007/api/proxy/reuters-news-feed/?best-topics=tech&post_type=best"
title="Reuters News"
paging={false} // Optional. By default the paging is enabled, but it can be disabled
/>
</Grid>
...
);
};
```

## Customization

It's also possible to customize each row in the RSS Feed table. You have to use the property `rowRenderer`. Check the [default renderer](./src/RSSCard/Content.tsx#L65-L93) as an example.
2 changes: 1 addition & 1 deletion plugins/home/backstage-plugin-home-rss/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
"@backstage/config": "^1.0.8",
"@backstage/core-components": "^0.13.2",
"@backstage/core-plugin-api": "^1.5.2",
"@backstage/plugin-home": "^0.5.3",
"@backstage/plugin-home-react": "^0.1.0",
"@material-ui/core": "^4.12.3",
"@material-ui/lab": "^4.0.0-alpha.60",
"cross-fetch": "^3.1.4",
Expand Down
96 changes: 39 additions & 57 deletions plugins/home/backstage-plugin-home-rss/src/RSSCard/Content.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@
*/

import React from 'react';
import { ErrorPanel, Table } from '@backstage/core-components';
import { RSSContentProps } from './types';
import { ErrorPanel, Link, Table } from '@backstage/core-components';
import { DataItem, RSSContentProps } from './types';
import { useAsync } from 'react-use';
import { Box, Typography, Link, makeStyles } from '@material-ui/core';
import { Typography, makeStyles } from '@material-ui/core';
import { DateTime } from 'luxon';
import { Skeleton } from '@material-ui/lab';

Expand All @@ -34,10 +34,6 @@ const useStyles = makeStyles(theme => ({
},
}));

type DataItem = {
title: any;
};

const columns = [
{
title: '',
Expand All @@ -54,53 +50,35 @@ const skeletonDataItem = {
</>
),
};
const skeletonData = [
skeletonDataItem,
skeletonDataItem,
skeletonDataItem,
skeletonDataItem,
skeletonDataItem,
];
const skeletonData = Array(6).fill(skeletonDataItem);

/**
* A component to render a RSS feed
* A component to render an RSS feed
*
* @public
*/
export const Content = (props: RSSContentProps) => {
const parser = new DOMParser();
const classes = useStyles();
const { feedURL, paging = true, rowRenderer } = props;

const { value, loading, error } = useAsync(async () => {
const headers = new Headers({
Accept: 'application/rss+xml',
});

const response = await fetch(props.feedURL, { headers: headers });

const body = await response.text();
const feedData = parser.parseFromString(body, 'application/xml');
const title = feedData.querySelector('title')?.textContent || undefined;

const items = feedData.querySelectorAll('item');
const defaultRow = (items: NodeListOf<Element>): DataItem[] => {
const result: DataItem[] = [];
items.forEach(item => {
const link = item.querySelector('link')?.textContent;
const itemTitle = item.querySelector('title')?.textContent;
const pubDate = item.querySelector('pubDate')?.textContent;
let pubDateString: string | undefined = undefined;
if (pubDate) {
const publishedAt = DateTime.fromRFC2822(pubDate);
pubDateString = publishedAt.toLocaleString(DateTime.DATE_MED);
}
const pubDateString = pubDate
? DateTime.fromRFC2822(pubDate).toLocaleString(DateTime.DATE_MED)
: undefined;

if (link && itemTitle) {
const itemComponent = (
<>
<Typography className={classes.newsItemDate}>
{pubDateString}
</Typography>
<Link className={classes.newsItemLink} href={link} target="_blank">
<Link className={classes.newsItemLink} to={link} target="_blank">
{itemTitle}
</Link>
</>
Expand All @@ -111,34 +89,38 @@ export const Content = (props: RSSContentProps) => {
});
}
});
return { data: result, title };
return result;
};

const { value = skeletonData, error } = useAsync(async () => {
const headers = new Headers({
Accept: 'application/rss+xml',
});

const response = await fetch(feedURL, { headers: headers });

const body = await response.text();
const feedData = parser.parseFromString(body, 'application/xml');
const items = feedData.querySelectorAll('item');

return rowRenderer ? await rowRenderer(items) : defaultRow(items);
}, []);

if (error) {
return <ErrorPanel error={error} />;
}
let tableData: DataItem[] = [];
let title;
if (loading) {
tableData = skeletonData;
title = <Skeleton variant="text" width={200} />;
} else if (value) {
tableData = value.data;
title = value.title;
}

return (
<Box position="relative">
<Table
title={title}
options={{
search: false,
paging: true,
showTitle: true,
padding: 'dense',
header: false,
}}
data={tableData}
columns={columns}
/>
</Box>
<Table
options={{
search: false,
paging: paging,
toolbar: false,
padding: 'dense',
header: false,
}}
data={value}
columns={columns}
/>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@
*/

export { Content } from './Content';
export { type DataItem } from './types';
13 changes: 13 additions & 0 deletions plugins/home/backstage-plugin-home-rss/src/RSSCard/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,24 @@
* limitations under the License.
*/

/**
* Element displayed in each row for the RSS Feed {@link Content}.
*
* @public
*/
export type DataItem = {
title: JSX.Element;
};

/**
* Props for RSS content component {@link Content}.
*
* @public
*/
export type RSSContentProps = {
feedURL: string;
paging?: boolean;
rowRenderer?: (
items: NodeListOf<Element>,
) => DataItem[] | Promise<DataItem[]>;
};
1 change: 1 addition & 0 deletions plugins/home/backstage-plugin-home-rss/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@
*/

export { HomePageRSS, rssPlugin } from './plugin';
export { type DataItem } from './RSSCard';
9 changes: 4 additions & 5 deletions plugins/home/backstage-plugin-home-rss/src/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,10 @@
* limitations under the License.
*/

import { createCardExtension } from '@backstage/plugin-home';
import { createCardExtension } from '@backstage/plugin-home-react';
import { createPlugin } from '@backstage/core-plugin-api';
import { rootRouteRef } from './routes';
import { RSSContentProps } from './RSSCard/types';

/** @public */
export const rssPlugin = createPlugin({
Expand All @@ -32,11 +33,9 @@ export const rssPlugin = createPlugin({
* @public
*/
export const HomePageRSS = rssPlugin.provide(
createCardExtension<{
feedURL: string;
}>({
createCardExtension<RSSContentProps>({
name: 'HomePageRSS',
title: '',
title: 'RSS Feed',
components: () => import('./RSSCard'),
}),
);

0 comments on commit 58b77fa

Please sign in to comment.