Skip to content

Commit

Permalink
Update reply text and unit tests
Browse files Browse the repository at this point in the history
  • Loading branch information
aterstriep committed Nov 15, 2024
1 parent 6004813 commit 829b65c
Show file tree
Hide file tree
Showing 8 changed files with 260 additions and 23 deletions.
10 changes: 7 additions & 3 deletions src/components/DiscussionPost/index.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,16 @@ describe('DiscussionPost', () => {
const dateText = getRelativeDate(createdAt);
expect(screen.getByText(dateText)).toBeInTheDocument();

expect(screen.getByRole('button', { name: 'Reply' })).toBeInTheDocument();

const repliesCount = replies.length;
expect(
screen.getByText(`${repliesCount} replies in this discussion`)
screen.getByRole('button', { name: `${repliesCount} replies` })
).toBeInTheDocument();

const lastReplyAtText = i18next.t('discussions:general.lastReply', {
date: getRelativeDate(replies[0].createdAt, 1),
time: '10:00 AM'
});
expect(screen.getByText(lastReplyAtText)).toBeInTheDocument();
});

it('displays roles fallback text', () => {
Expand Down
35 changes: 29 additions & 6 deletions src/components/DiscussionPost/index.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import React from 'react';
import React, { useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { Icon } from '@trussworks/react-uswds';
import { SystemIntakeGRBReviewDiscussionFragment } from 'gql/gen/graphql';
import { upperFirst } from 'lodash';
import { DateTime } from 'luxon';

import { RichTextViewer } from 'components/RichTextEditor';
import { AvatarCircle } from 'components/shared/Avatar/Avatar';
Expand Down Expand Up @@ -36,6 +38,25 @@ const DiscussionPost = ({
? `${t(`grbReview:votingRoles.${votingRole}`)}, ${t(`grbReview:reviewerRoles.${grbRole}`)}`
: t('governanceReviewBoard.governanceAdminTeam');

/**
* Formatted text for date and time of last reply
*
* If more than one day since reply, uses formatted date.
* Otherwise, uses relative date.
*/
const lastReplyAtText = useMemo(() => {
if (replies.length === 0) return '';

const [lastReply] = replies;

const dateTime = DateTime.fromISO(lastReply.createdAt);

return t('general.lastReply', {
date: getRelativeDate(lastReply.createdAt, 1),
time: dateTime.toLocaleString(DateTime.TIME_SIMPLE)
});
}, [replies, t]);

return (
<div className="easi-discussion-reply display-flex">
<div className="margin-right-105">
Expand All @@ -53,7 +74,7 @@ const DiscussionPost = ({
</div>

<p className="margin-top-105 tablet:margin-top-0 margin-bottom-0 text-base">
{getRelativeDate(createdAt)}
{upperFirst(getRelativeDate(createdAt))}
</p>
</div>

Expand All @@ -76,12 +97,14 @@ const DiscussionPost = ({
icon={<Icon.Announcement className="text-primary" />}
unstyled
>
{t('general.reply')}
{replies.length > 0
? t('general.repliesCount', { count: replies.length })
: t('general.reply')}
</IconButton>

<p className="text-base margin-0">
{t('general.repliesInDiscussion', { count: replies.length })}
</p>
{replies.length > 0 && (
<p className="text-base margin-0">{lastReplyAtText}</p>
)}
</div>
</div>
</div>
Expand Down
6 changes: 3 additions & 3 deletions src/data/mock/discussions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ const mockDiscussions = (
commonName: users[5].commonName
},
systemIntakeID,
createdAt: '2024-11-12T21:59:07.368862Z'
createdAt: '2024-11-12T10:00:00.368862Z'
},
replies: [
{
Expand All @@ -41,7 +41,7 @@ const mockDiscussions = (
commonName: users[1].commonName
},
systemIntakeID,
createdAt: '2024-11-13T21:59:07.368862Z'
createdAt: '2024-11-13T10:00:00.368862Z'
},
{
__typename: 'SystemIntakeGRBReviewDiscussionPost',
Expand All @@ -56,7 +56,7 @@ const mockDiscussions = (
commonName: users[7].commonName
},
systemIntakeID,
createdAt: '2024-11-13T21:59:07.368862Z'
createdAt: '2024-11-13T10:00:00.368862Z'
}
]
}
Expand Down
3 changes: 3 additions & 0 deletions src/i18n/en-US/discussions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,10 @@ const discussions = {
readMore: 'Read more', // TODO: this is in other i18n files, move to general.ts?
repliesInDiscussion: '{{count}} reply in this discussion',
repliesInDiscussion_plural: '{{count}} replies in this discussion',
repliesCount: '{{count}} reply',
repliesCount_plural: '{{count}} replies',
reply: 'Reply',
lastReply: 'Last reply {{date}} at {{time}}',
saveDiscussion: 'Save discussion',

startDiscussion: 'Start a new discussion',
Expand Down
2 changes: 1 addition & 1 deletion src/utils/date.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,6 @@ describe('getRelativeDate', () => {

const relativeDate = getRelativeDate(date.toISO());

expect(relativeDate).toEqual('Today');
expect(relativeDate).toEqual('today');
});
});
25 changes: 16 additions & 9 deletions src/utils/date.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { upperFirst } from 'lodash';
import { DateTime, Interval } from 'luxon';

// Used to parse out mintute, day, ,month, and years from ISOString
Expand Down Expand Up @@ -75,27 +74,35 @@ export const isDateInPast = (date: string | null): boolean => {
};

/**
* If less than 30 days have passed since `date`, returns "Today" or "X days ago".
* If less than 30 days have passed since `date`, returns "today" or "X days ago".
*
* Otherwise, returns formatted date.
*/
export const getRelativeDate = (date: string | null): string => {
export const getRelativeDate = (
date: string | null,
/**
* Number of days between `date` and now to display relative date
* before switching to formatted date
*/
relativeDateLimit: number = 30
): string => {
if (!date) return '';

const dateTime = DateTime.fromISO(date);

if (!dateTime.isValid) return '';

/** Interval between now and `date` */
const interval = Interval.fromISO(`${date}/${DateTime.now().toISO()}`);
const interval = Interval.fromDateTimes(dateTime, DateTime.now());

// Subtract one from the interval count to see how many days since the initial date
const days = interval.count('days') - 1;

// If more than 30 days have passed, return formatted date
if (days >= 30) {
if (days > relativeDateLimit) {
return DateTime.fromISO(date).toFormat('MM/dd/yyyy');
}

const rtf = new Intl.RelativeTimeFormat('en', { numeric: 'auto' });

// Return relative date
// Uses `UpperFirst` to capitalize first letter of "Today"
return upperFirst(rtf.format(-days, 'days'));
return dateTime.toRelativeCalendar({ unit: 'days' });
};
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ describe('Discussions', () => {
<Discussions grbDiscussions={mockDiscussions()} />
);

expect(asFragment).toMatchInlineSnapshot(`[Function]`);
expect(asFragment()).toMatchSnapshot(`[Function]`);
});

it('renders discussion with replies', () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html

exports[`Discussions > matches the snapshot > [Function] 1`] = `
<DocumentFragment>
<div
class="grb-discussions bg-base-lightest padding-x-3 padding-y-3 radius-md"
>
<h2
class="margin-top-0 margin-bottom-1"
>
Discussions
</h2>
<p
class="margin-top-0 line-height-body-5"
>
Use the discussion boards below to discuss this project. The internal GRB discussion board is a space for the Governance Admin Team and GRB members to discuss privately; the project team will not be able to view discussions there.
</p>
<div
class="easi-collapsable-link"
>
<button
aria-controls="discussionBoardTips"
aria-expanded="false"
class="usa-button usa-button--unstyled display-flex flex-align-center text-bold"
data-testid="collapsable-link"
type="button"
>
<svg
class="usa-icon margin-right-05"
focusable="false"
height="1em"
role="img"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M0 0h24v24H0z"
fill="none"
/>
<path
d="M10 6 8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"
/>
</svg>
Tips for using the discussion boards
</button>
</div>
<div
class="internal-discussion-board bg-white padding-3 padding-bottom-4 margin-top-4 border-base-lighter shadow-2"
>
<div
class="internal-discussions-board__header desktop:display-flex flex-align-start"
>
<h3
class="margin-top-0 margin-bottom-1"
>
Internal GRB discussion board
</h3>
<p
class="margin-0 margin-top-05 text-base display-flex text-no-wrap"
>
<svg
class="usa-icon margin-right-05"
focusable="false"
height="1em"
role="img"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M0 0h24v24H0z"
fill="none"
/>
<path
d="M12 17c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm6-9h-1V6c0-2.76-2.24-5-5-5S7 3.24 7 6v2H6c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V10c0-1.1-.9-2-2-2zM8.9 6c0-1.71 1.39-3.1 3.1-3.1s3.1 1.39 3.1 3.1v2H8.9V6zM18 20H6V10h12v10z"
/>
</svg>
<span>
Visibility restricted
</span>
</p>
<button
class="usa-button usa-button--outline margin-right-0 margin-y-2 desktop:margin-y-0 text-no-wrap"
data-testid="button"
type="button"
>
View discussion board
</button>
</div>
<div
class="display-flex"
>
<p
class="margin-0 margin-right-105 display-flex"
>
0 discussions without replies
</p>
</div>
<h4
class="margin-bottom-2"
>
Most recent activity
</h4>
<div
class="easi-discussion-reply display-flex"
>
<div
class="margin-right-105"
>
<div
class="display-flex flex-align-center flex-justify-center minw-4 circle-4 bg-red-cool-10 "
data-testid="avatar--basic"
>
OA
</div>
</div>
<div
class="width-full"
>
<div
class="easi-discussion-reply__header tablet:display-flex margin-top-1 margin-bottom-105"
>
<div>
<p
class="margin-y-0"
>
Otilia Abbott
</p>
<h5
class="margin-top-05 margin-bottom-0 font-body-xs text-base text-normal"
>
Voting, Subject Matter Expert (SME)
</h5>
</div>
<p
class="margin-top-105 tablet:margin-top-0 margin-bottom-0 text-base"
>
3 days ago
</p>
</div>
<div
class="easi-toast easi-toast-viewer easi-discussion-reply__content"
>
<div>
<div
class="toastui-editor-contents"
style="overflow-wrap: break-word;"
>
<div
data-nodeid="1"
>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
</p>
</div>
</div>
</div>
</div>
<div
class="easi-discussion-reply__footer display-flex margin-top-2"
>
<button
class="usa-button usa-button--unstyled display-flex flex-align-center margin-right-205"
data-testid="button"
type="button"
>
<svg
class="usa-icon text-primary margin-right-1"
focusable="false"
height="1em"
role="img"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M0 0h24v24H0z"
fill="none"
/>
<path
d="M20 2H4c-1.1 0-1.99.9-1.99 2L2 22l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-7 9h-2V5h2v6zm0 4h-2v-2h2v2z"
/>
</svg>
2 replies
</button>
<p
class="text-base margin-0"
>
Last reply 11/13/2024 at 10:00 AM
</p>
</div>
</div>
</div>
</div>
</div>
</DocumentFragment>
`;

0 comments on commit 829b65c

Please sign in to comment.