Skip to content

Commit

Permalink
Merge pull request #2386 from HHS/main
Browse files Browse the repository at this point in the history
[Prod] Strip whitespace from beginning and end of string when creating RTR objectives, improve RSS feed processing, properly save source on RTR
  • Loading branch information
Jones-QuarteyDana authored Sep 25, 2024
2 parents e309fc6 + 17dac9d commit c1a699d
Show file tree
Hide file tree
Showing 15 changed files with 235 additions and 121 deletions.
3 changes: 2 additions & 1 deletion frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@
"detect-file-encoding-and-language": "^2.4.0",
"draft-js": "^0.11.7",
"draftjs-to-html": "^0.9.1",
"ejs": "^3.1.10",
"ejs": "^3.1.7",
"html-react-parser": "^5.1.1",
"follow-redirects": "^1.15.6",
"html-to-draftjs": "^1.5.0",
"html2canvas": "^1.4.1",
Expand Down
5 changes: 4 additions & 1 deletion frontend/src/components/ContentFromFeedByTag.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export default function ContentFromFeedByTag({
tagName,
contentSelector,
className,
openLinksInNewTab,
}) {
const [content, setContent] = useState('');

Expand Down Expand Up @@ -62,7 +63,7 @@ export default function ContentFromFeedByTag({

return (
<div className={classNames}>
<FeedArticle title="" content={content} unread={false} key={content} partial />
<FeedArticle title="" content={content} unread={false} key={content} openLinksInNewTab={openLinksInNewTab} partial />
</div>
);
}
Expand All @@ -71,9 +72,11 @@ ContentFromFeedByTag.propTypes = {
tagName: PropTypes.string.isRequired,
contentSelector: PropTypes.string,
className: PropTypes.string,
openLinksInNewTab: PropTypes.bool,
};

ContentFromFeedByTag.defaultProps = {
contentSelector: '',
className: '',
openLinksInNewTab: false,
};
42 changes: 39 additions & 3 deletions frontend/src/components/FeedArticle.js
Original file line number Diff line number Diff line change
@@ -1,21 +1,55 @@
import React from 'react';
import React, { useLayoutEffect } from 'react';
import PropTypes from 'prop-types';
import ReadOnlyEditor from './ReadOnlyEditor';
import parse from 'html-react-parser';
import './FeedArticle.scss';

const TAG_CLASSES = {
P: 'usa-prose',
TABLE: 'usa-table',
UL: 'usa-list',
};

const FeedArticle = ({
title,
content,
unread,
partial,
openLinksInNewTab,
}) => {
/**
* to match the styling in the design system, we attach USWDS classes to the
* appropriate elements
*/
useLayoutEffect(() => {
const tags = document.querySelectorAll(`
.ttahub-feed-article-content p,
.ttahub-feed-article-content table,
.ttahub-feed-article-content ul
`);

Array.from(tags).forEach((tag) => {
const tagClass = TAG_CLASSES[tag.tagName];
if (tagClass) {
tag.classList.add(tagClass);
}
});

if (openLinksInNewTab) {
const links = document.querySelectorAll('.ttahub-feed-article-content a');
Array.from(links).forEach((link) => {
link.setAttribute('target', '_blank');
link.setAttribute('rel', 'noopener noreferrer');
});
}
});

const className = `ttahub-feed-article ${partial ? 'ttahub-feed-article--partial' : ''}`;

return (
<article className={`${className} position-relative margin-bottom-3 padding-bottom-3 ${unread ? 'ttahub-feed-article--unread' : ''}`}>
<div className="ttahub-feed-article-content position-relative maxw-tablet">
<h4 className="ttahub-feed-article-title usa-prose margin-0 padding-0">{title}</h4>
<ReadOnlyEditor value={content} ariaLabel={title} />
{parse(content)}
</div>
</article>
);
Expand All @@ -26,10 +60,12 @@ FeedArticle.propTypes = {
content: PropTypes.string.isRequired,
unread: PropTypes.bool.isRequired,
partial: PropTypes.bool,
openLinksInNewTab: PropTypes.bool,
};

FeedArticle.defaultProps = {
partial: false,
openLinksInNewTab: false,
};

export default FeedArticle;
42 changes: 9 additions & 33 deletions frontend/src/components/FeedArticle.scss
Original file line number Diff line number Diff line change
@@ -1,39 +1,15 @@
@use '../colors.scss' as *;

/**
this hides the initial "author inforation" blocks"
**/
.ttahub-feed-article:not(.ttahub-feed-article--partial) div[data-contents="true"] > [data-block="true"]:nth-child(1),
.ttahub-feed-article:not(.ttahub-feed-article--partial) div[data-contents="true"] > [data-block="true"]:nth-child(2),
.ttahub-feed-article:not(.ttahub-feed-article--partial) div[data-contents="true"] > [data-block="true"]:nth-child(3),
.ttahub-feed-article:not(.ttahub-feed-article--partial) div[data-contents="true"] > [data-block="true"]:last-child,
.ttahub-feed-article div[data-contents="true"] > [data-block="true"]:empty,
.ttahub-feed-article h4:empty {
// hide the "blog post by" text
.ttahub-feed-article-content p:has(a:not([href])){
display: none;
}


// fix the margins and spacing of the read only editor
.ttahub-feed-article .public-DraftStyleDefault-block {
font-size: 1.06rem;
line-height: 1.5;
margin: 0;
white-space: normal;
// remove inline styles from feed
// - since they are inline, we have to use !important
.ttahub-feed-article-content .feed > div {
border: none !important;
padding: 0 !important;
}

// hide the big old link icon that pops up
.ttahub-feed-article .rdw-link-decorator-icon {
.ttahub-feed-article-content .feed div:last-child{
display: none;
}

.ttahub-feed-article .public-DraftStyleDefault-block a:after,
.ttahub-read-more--external:after {
content: "\f35d";
display: inline-block;
font: normal normal normal 16px/1 FontAwesome;
margin: 0 8px;
}

.ttahub-feed-article .public-DraftStyleDefault-block a:hover:after {
opacity: 0.75;
}
}
1 change: 0 additions & 1 deletion frontend/src/components/GoalForm/ObjectiveTopics.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import Drawer from '../Drawer';
import Req from '../Req';
import ContentFromFeedByTag from '../ContentFromFeedByTag';
import DrawerTriggerButton from '../DrawerTriggerButton';
import './ObjectiveTopics.scss';

export default function ObjectiveTopics({
error,
Expand Down
9 changes: 0 additions & 9 deletions frontend/src/components/GoalForm/ObjectiveTopics.scss

This file was deleted.

2 changes: 1 addition & 1 deletion frontend/src/components/GoalForm/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ export default function GoalForm({
useDeepCompareEffect(() => {
const newPrompts = grantsToMultiValue(selectedGrants, { ...prompts });
if ((!isEqual(newPrompts, prompts))) {
setSource(newPrompts);
setPrompts(newPrompts);
}
}, [prompts, selectedGrants]);

Expand Down
3 changes: 1 addition & 2 deletions frontend/src/components/SupportTypeDrawer.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import React from 'react';
import PropTypes from 'prop-types';
import Drawer from './Drawer';
import ContentFromFeedByTag from './ContentFromFeedByTag';
import './SupportTypeDrawer.scss';

export default function SupportTypeDrawer({
drawerTriggerRef,
Expand All @@ -14,7 +13,7 @@ export default function SupportTypeDrawer({
stickyFooter
title="Support type guidance"
>
<ContentFromFeedByTag className="ttahub-drawer--objective-support-type-guidance" tagName="ttahub-tta-support-type" contentSelector="table" />
<ContentFromFeedByTag openLinksInNewTab className="ttahub-drawer--objective-support-type-guidance" tagName="ttahub-tta-support-type" />
</Drawer>
);
}
Expand Down
65 changes: 0 additions & 65 deletions frontend/src/components/SupportTypeDrawer.scss

This file was deleted.

84 changes: 82 additions & 2 deletions frontend/src/components/__tests__/ContentFromFeedByTag.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,15 @@ const DEFAULT_RESPONSE = `<?xml version="1.0" encoding="UTF-8"?>
describe('ContentFromFeedByTag', () => {
afterEach(() => fetchMock.restore());

const renderContentFromFeed = (tag = 'tag', content = DEFAULT_RESPONSE, selector = null) => {
const renderContentFromFeed = (
tag = 'tag',
content = DEFAULT_RESPONSE,
selector = null,
openLinksInNewTab = false,
) => {
fetchMock.get(`/api/feeds/item?tag=${tag}`, content);
render(<ContentFromFeedByTag tagName={tag} contentSelector={selector} />);
// eslint-disable-next-line max-len
render(<ContentFromFeedByTag tagName={tag} contentSelector={selector} openLinksInNewTab={openLinksInNewTab} />);
};

it('renders a feed', async () => {
Expand Down Expand Up @@ -112,4 +118,78 @@ describe('ContentFromFeedByTag', () => {
expect(readOnly).toBeTruthy();
});
});

it('properly formats support type response (as an example)', async () => {
const supportTypeResponse = `<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
<title>Tag ttahub-tta-support-type</title>
<link rel="alternate" href="https://acf-ohs.atlassian.net/wiki" />
<subtitle>Confluence Syndication Feed</subtitle>
<id>https://acf-ohs.atlassian.net/wiki</id>
<entry>
<title>OHS guidance on TTA support types</title>
<link rel="alternate" href="https://acf-ohs.atlassian.net/wiki/spaces/OHSTTA/pages/184516720/OHS+guidance+on+TTA+support+types" />
<category term="objectives" />
<category term="goals" />
<category term="ar" />
<category term="guidance" />
<category term="userguide" />
<category term="ttahub-tta-support-type" />
<author>
<name>User Author</name>
</author>
<id>tag:acf-ohs.atlassian.net,2009:page-184516720-11</id>
<updated>2024-09-13T15:11:34Z</updated>
<published>2024-09-13T15:11:34Z</published>
<summary type="html">&lt;div class="feed"&gt; &lt;p&gt;
Page
&lt;b&gt;edited&lt;/b&gt; by
&lt;a &gt;User Author&lt;/a&gt;
&lt;/p&gt;
&lt;div style="border-top: 1px solid #ddd; border-bottom: 1px solid #ddd; padding: 10px;"&gt;
&lt;p&gt;Support types are assigned at the objective level for each AR or TR. &lt;/p&gt;&lt;p /&gt;&lt;h2 id="OHSguidanceonTTAsupporttypes-TTAsupporttype"&gt;&lt;strong&gt;TTA support type&lt;/strong&gt;&lt;/h2&gt;&lt;p&gt;TTA support types describe the content of the TTA session and what that content was intended to support the recipient to accomplish. While the support types do build upon each other, there is no expectation that every goal will start with “Introducing” and end with “Maintaining”. Specialists should use the support types to describe the highest level of support offered during the session. When selecting support types consider how the content supports the recipient with:&lt;/p&gt;&lt;h3 id="OHSguidanceonTTAsupporttypes-Introducing"&gt;&lt;strong&gt;Introducing&lt;/strong&gt;&lt;/h3&gt;&lt;p&gt;Introducing and assessing knowledge and/or awareness of concepts, subject matter, and practices.&lt;/p&gt;&lt;p&gt;Use this type for TTA activities that include content such as:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;introducing concepts or new practices&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;assessing participant knowledge&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;needs assessments&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h3 id="OHSguidanceonTTAsupporttypes-Planning"&gt;&lt;strong&gt;Planning&lt;/strong&gt;&lt;/h3&gt;&lt;p&gt;Planning to initiate new/revised services and systems.&lt;/p&gt;&lt;p&gt;Use this type for TTA activities that include content that involves participants developing a plan such as a coaching plan, staff wellness plan, corrective action plan, etc.&lt;/p&gt;&lt;h3 id="OHSguidanceonTTAsupporttypes-Implementing"&gt;&lt;strong&gt;Implementing&lt;/strong&gt;&lt;/h3&gt;&lt;p&gt;Implementing new/revised services and systems.&lt;/p&gt;&lt;p&gt;Use this type for TTA activities that include content such as:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;developing or revising policies and procedures to address a service/system&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;training for staff to implement a new initiative&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h3 id="OHSguidanceonTTAsupporttypes-Maintaining"&gt;&lt;strong&gt;Maintaining&lt;/strong&gt;&lt;/h3&gt;&lt;p&gt;Maintaining and monitoring services and systems and ensuring ongoing quality improvement.&lt;/p&gt;&lt;p&gt;Use this type for TTA activities that include content such as:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;reviewing data to identify needed course corrections&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;reviewing ongoing monitoring data&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;conducting ongoing monitoring&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;reviewing progress on program goals&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;hr/&gt;&lt;h2 style="text-align: center;" id="OHSguidanceonTTAsupporttypes-Needmorehelp?"&gt;Need more help?&lt;/h2&gt;&lt;p style="text-align: center;"&gt;If you can’t find an answer, &lt;a class="external-link" href="https://app.smartsheetgov.com/b/form/f0b4725683f04f349a939bd2e3f5425a" rel="nofollow"&gt;contact support&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div style="padding: 10px 0;"&gt;
&lt;a href="https://acf-ohs.atlassian.net/wiki/spaces/OHSTTA/pages/184516720/OHS+guidance+on+TTA+support+types"&gt;email.notification.view.online&lt;/a&gt;
&amp;middot;
&lt;a href="https://acf-ohs.atlassian.net/wiki/pages/diffpagesbyversion.action?pageId=184516720&amp;revisedVersion=11&amp;originalVersion=10"&gt;View Changes Online&lt;/a&gt;
&lt;/div&gt;
&lt;/div&gt;</summary>
<dc:creator>User Author</dc:creator>
<dc:date>2024-09-13T15:11:34Z</dc:date>
</entry>
</feed>`;

act(() => {
renderContentFromFeed(
'tag',
supportTypeResponse,
null,
true,
);
});

const article = document.querySelector('.ttahub-feed-article-content');
expect(article).not.toBeNull();
const lists = article.querySelectorAll('ul');
lists.forEach((list) => {
expect(list).toHaveClass('usa-list');
});

const tables = article.querySelectorAll('table');
tables.forEach((table) => {
expect(table).toHaveClass('usa-table');
});

const paragraphs = article.querySelectorAll('p');
paragraphs.forEach((paragraph) => {
expect(paragraph).toHaveClass('usa-prose');
});

const links = article.querySelectorAll('a');
links.forEach((link) => {
expect(link).toHaveAttribute('target', '_blank');
expect(link).toHaveAttribute('rel', 'noopener noreferrer');
});
});
});
4 changes: 4 additions & 0 deletions frontend/src/pages/Notifications/components/WhatsNew.scss
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@
padding-left: 2rem;
}

.ttahub-feed-whats-new .ttahub-feed-article-content .feed div:last-child{
display: none;
}

// this adds the little green circle to the left of the "new" notification
.ttahub-feed-whats-new .ttahub-feed-article--unread .ttahub-feed-article-content:before {
content: '';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,10 @@
margin-bottom: 12px;
padding-bottom: 0;
}

.ttahub-class-feed-article .ttahub-feed-article p:has(strong){
text-align: left;
}
.ttahub-class-feed-article .ttahub-feed-article th .usa-prose {
margin: 0;
}
Loading

0 comments on commit c1a699d

Please sign in to comment.