Skip to content

Commit

Permalink
feat: display date as time has passed (#3840)
Browse files Browse the repository at this point in the history
  • Loading branch information
onim-at committed Sep 6, 2024
1 parent f4c3e40 commit f34497f
Show file tree
Hide file tree
Showing 13 changed files with 137 additions and 74 deletions.
26 changes: 4 additions & 22 deletions packages/components/src/CommentItem/CommentItem.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { createRef, useEffect, useState } from 'react'
import { format, formatDistanceToNow } from 'date-fns'
import { Avatar, Box, Flex, Text } from 'theme-ui'

import defaultProfileImage from '../../assets/images/default_member.svg'
import { Button } from '../Button/Button'
import { ConfirmModal } from '../ConfirmModal/ConfirmModal'
import { DisplayDate } from '../DisplayDate/DisplayDate'
import { EditComment } from '../EditComment/EditComment'
import { LinkifyText } from '../LinkifyText/LinkifyText'
import { Modal } from '../Modal/Modal'
Expand All @@ -23,20 +23,6 @@ export interface IProps {
isReply: boolean
}

const formatDate = (d: string | undefined): string => {
if (!d) {
return ''
}
return format(new Date(d), 'dd MMMM yyyy h:mm a')
}

const relativeDateFormat = (d: string | undefined): string => {
if (!d) {
return ''
}
return formatDistanceToNow(new Date(d), { addSuffix: true })
}

export const CommentItem = (props: IProps) => {
const textRef = createRef<any>()
const [showEditModal, setShowEditModal] = useState(false)
Expand Down Expand Up @@ -66,8 +52,6 @@ export const CommentItem = (props: IProps) => {
isSupporter: !!isUserSupporter,
}

const date = formatDate(_edited || _created)
const relativeDate = relativeDateFormat(_edited || _created)
const maxHeight = isShowMore ? 'max-content' : '128px'
const item = isReply ? 'ReplyItem' : 'CommentItem'

Expand Down Expand Up @@ -132,11 +116,9 @@ export const CommentItem = (props: IProps) => {
}}
>
<Username user={user} />
{_edited && (
<Text sx={{ fontSize: 0, color: 'grey' }}>(Edited)</Text>
)}
<Text sx={{ fontSize: 1 }} title={date}>
{relativeDate}
<Text sx={{ fontSize: 1, color: 'darkGrey' }}>
{_edited && 'Edited '}
<DisplayDate date={_edited || _created} />
</Text>
</Flex>

Expand Down
23 changes: 23 additions & 0 deletions packages/components/src/DisplayDate/DisplayDate.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { subMonths } from 'date-fns'

import { DisplayDate } from './DisplayDate'

import type { Meta, StoryFn } from '@storybook/react'

export default {
/* 👇 The title prop is optional.
* See https://storybook.js.org/docs/react/configure/overview#configure-story-loading
* to learn how to generate automatic titles
*/
title: 'Components/DisplayDate',
component: DisplayDate,
} as Meta<typeof DisplayDate>

export const Default: StoryFn<typeof DisplayDate> = () => {
return <DisplayDate date={new Date()}></DisplayDate>
}

export const TwoMonthsAGo: StoryFn<typeof DisplayDate> = () => {
const twoMonthsAGo = subMonths(new Date(), 2)
return <DisplayDate date={twoMonthsAGo}></DisplayDate>
}
24 changes: 24 additions & 0 deletions packages/components/src/DisplayDate/DisplayDate.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import '@testing-library/jest-dom/vitest'

import { describe, expect, it } from 'vitest'

import { render } from '../test/utils'
import { Default, TwoMonthsAGo } from './DisplayDate.stories'

import type { IProps } from './DisplayDate'

describe('DisplayDate', () => {
it('renders correctly current date', () => {
const { getByText } = render(<Default {...(TwoMonthsAGo.args as IProps)} />)

expect(getByText('less than a minute ago')).toBeInTheDocument()
})

it('renders correctly when two months ago', () => {
const { getByText } = render(
<TwoMonthsAGo {...(TwoMonthsAGo.args as IProps)} />,
)

expect(getByText('2 months ago')).toBeInTheDocument()
})
})
30 changes: 30 additions & 0 deletions packages/components/src/DisplayDate/DisplayDate.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { format, formatDistanceToNow } from 'date-fns'
import { Text } from 'theme-ui'

type DateType = string | number | Date

export interface IProps {
date?: DateType
}

const formatDateTime = (date: DateType | undefined) => {
if (!date) {
return ''
}

return format(new Date(date), 'dd-MM-yyyy HH:mm')
}

const relativeDateFormat = (d: DateType | undefined): string => {
if (!d) {
return ''
}
return formatDistanceToNow(new Date(d), { addSuffix: true })
}

export const DisplayDate = ({ date }: IProps) => {
const formattedDate = formatDateTime(date)
const relativeDate = relativeDateFormat(date)

return <Text title={formattedDate}>{relativeDate}</Text>
}
1 change: 1 addition & 0 deletions packages/components/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export { ConfirmModal } from './ConfirmModal/ConfirmModal'
export { CreateComment } from './CreateComment/CreateComment'
export { DiscussionContainer } from './DiscussionContainer/DiscussionContainer'
export { DiscussionTitle } from './DiscussionTitle/DiscussionTitle'
export { DisplayDate } from './DisplayDate/DisplayDate'
export { DonationRequest } from './DonationRequest/DonationRequest'
export { DonationRequestModal } from './DonationRequestModal/DonationRequestModal'
export { DownloadButton } from './DownloadButton/DownloadButton'
Expand Down
2 changes: 1 addition & 1 deletion packages/cypress/src/integration/howto/read.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ describe('[How To]', () => {
cy.title().should('eq', `${howto.title} - How-to - Community Platform`)
cy.get('[data-cy=how-to-basis]').then(($summary) => {
expect($summary).to.contain('howto_creator', 'Author')
expect($summary).to.contain('Last update on', 'Edit')
expect($summary).to.contain('Last update', 'Edit')
expect($summary).to.contain('Make an interlocking brick', 'Title')
expect($summary).to.contain(
'show you how to make a brick using the injection machine',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,13 @@ describe('[Questions.Discussions]', () => {
cy.addComment(newComment)
cy.contains(`${discussion.comments.length + 1} comments`)
cy.contains(newComment)
cy.contains('less than a minute ago')

cy.step('Can edit their comment')
cy.editDiscussionItem('CommentItem', updatedNewComment)
cy.contains(updatedNewComment)
cy.contains(newComment).should('not.exist')
cy.contains('Edited less than a minute ago')

cy.step('Can add reply')
cy.addReply(newReply)
Expand Down
40 changes: 30 additions & 10 deletions src/pages/Research/Content/ResearchArticle.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
import { ThemeProvider } from '@emotion/react'
import { faker } from '@faker-js/faker'
import { act, render, waitFor, within } from '@testing-library/react'
import { formatDistanceToNow } from 'date-fns'
import { Provider } from 'mobx-react'
import { ResearchUpdateStatus, UserRole } from 'oa-shared'
import { useResearchStore } from 'src/stores/Research/research.store'
Expand All @@ -19,7 +20,6 @@ import {
} from 'src/test/factories/ResearchItem'
import { FactoryUser } from 'src/test/factories/User'
import { testingThemeStyles } from 'src/test/utils/themeUtils'
import { formatDate } from 'src/utils/date'
import { describe, expect, it, vi } from 'vitest'

import ResearchArticle from './ResearchArticle'
Expand Down Expand Up @@ -263,11 +263,9 @@ describe('Research Article', () => {

it('does not show edit timestamp, when create displays the same value', async () => {
const created = faker.date.past()
const modified = new Date(created)
modified.setHours(15)
const update = FactoryResearchItemUpdate({
_created: created.toString(),
_modified: modified.toString(),
_modified: created.toString(),
title: 'A title',
description: 'A description',
})
Expand All @@ -280,22 +278,32 @@ describe('Research Article', () => {
updates: [update],
}),
})

// Act
const wrapper = getWrapper()

// Assert
await waitFor(() => {
expect(() =>
wrapper.getAllByText(`edited ${formatDate(modified)}`),
wrapper.getAllByText(
`${formatDistanceToNow(update._modified, { addSuffix: true })}`,
),
).toThrow()
})
await waitFor(() => {
expect(() =>
wrapper.getAllByText((content) => content.includes('edited')),
).toThrow()
expect(() =>
wrapper.getAllByText((content) => content.includes('created')),
).not.toThrow()
})
})

it('does show both created and edit timestamp, when different', async () => {
const modified = faker.date.future()
const modified = faker.date.past({ years: 1 })
const created = faker.date.past({ years: 2 })
const update = FactoryResearchItemUpdate({
_created: faker.date.past().toString(),
_created: created.toString(),
status: ResearchUpdateStatus.PUBLISHED,
_modified: modified.toString(),
title: 'A title',
Expand All @@ -314,11 +322,23 @@ describe('Research Article', () => {

// Act
const wrapper = getWrapper()

// Assert
await waitFor(() => {
expect(() =>
wrapper.getAllByText(`edited ${formatDate(modified)}`),
wrapper.getAllByText((content) => content.includes('created')),
).not.toThrow()
expect(() =>
wrapper.getAllByText(
`${formatDistanceToNow(created, { addSuffix: true })}`,
),
).not.toThrow()
expect(() =>
wrapper.getAllByText((content) => content.includes('edited')),
).not.toThrow()
expect(() =>
wrapper.getAllByText(
`${formatDistanceToNow(modified, { addSuffix: true })}`,
),
).not.toThrow()
})
})
Expand Down
24 changes: 13 additions & 11 deletions src/pages/Research/Content/ResearchListItem.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { useMemo } from 'react'
import {
Category,
DisplayDate,
Icon,
IconCountWithTooltip,
InternalLink,
Expand All @@ -14,7 +15,6 @@ import {
} from 'oa-shared'
import { useCommonStores } from 'src/common/hooks/useCommonStores'
import { cdnImageUrl } from 'src/utils/cdnImageUrl'
import { formatDate } from 'src/utils/date'
import { Box, Card, Flex, Grid, Heading, Image, Text } from 'theme-ui'

import defaultResearchThumbnail from '../../../assets/images/default-research-thumbnail.jpg'
Expand Down Expand Up @@ -182,7 +182,7 @@ const ResearchListItem = ({ item }: IProps) => {
</Text>
)}
{/* Hide this on mobile, show on tablet & above. */}
{modifiedDate && (
{modifiedDate !== '' && (
<Text
ml={4}
sx={{
Expand Down Expand Up @@ -301,19 +301,21 @@ const getItemThumbnail = (researchItem: IResearch.Item): string => {
)
}

const getItemDate = (item: IResearch.Item, variant: string): string => {
const getItemDate = (item: IResearch.Item, variant: string) => {
try {
const contentModifiedDate = formatDate(
new Date(item._contentModifiedTimestamp),
const contentModifiedDate = (
<DisplayDate date={item._contentModifiedTimestamp} />
)
const creationDate = formatDate(new Date(item._created))
const creationDate = <DisplayDate date={item._created} />

if (contentModifiedDate !== creationDate) {
return variant === 'long'
? `Updated ${contentModifiedDate}`
: contentModifiedDate
if (item._contentModifiedTimestamp !== item._created) {
return variant === 'long' ? (
<>Updated {contentModifiedDate}</>
) : (
contentModifiedDate
)
} else {
return variant === 'long' ? `Created ${creationDate}` : creationDate
return variant === 'long' ? <>Created {creationDate}</> : creationDate
}
} catch (err) {
return ''
Expand Down
11 changes: 4 additions & 7 deletions src/pages/Research/Content/ResearchUpdate.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Link, useNavigate } from 'react-router-dom'
import {
Button,
DisplayDate,
DownloadCounter,
DownloadFileFromLink,
DownloadStaticFile,
Expand All @@ -13,7 +14,6 @@ import {
import { useContributorsData } from 'src/common/hooks/contributorsData'
import { useCommonStores } from 'src/common/hooks/useCommonStores'
import { useResearchStore } from 'src/stores/Research/research.store'
import { formatDate } from 'src/utils/date'
import { formatImagesForGallery } from 'src/utils/formatImageListForGallery'
import { Box, Card, Flex, Heading, Text } from 'theme-ui'

Expand Down Expand Up @@ -51,8 +51,6 @@ const ResearchUpdate = (props: IProps) => {
const loggedInUser = useCommonStores().stores.userStore.activeUser

const contributors = useContributorsData(collaborators || [])
const formattedCreateDatestamp = formatDate(new Date(_created))
const formattedModifiedDatestamp = formatDate(new Date(_modified))
const research = researchStore.activeResearchItem

const handleDownloadClick = async () => {
Expand Down Expand Up @@ -154,18 +152,17 @@ const ResearchUpdate = (props: IProps) => {
textAlign: ['left', 'right', 'right'],
}}
>
{'created ' + formattedCreateDatestamp}
created <DisplayDate date={_created} />
</Text>

{formattedCreateDatestamp !==
formattedModifiedDatestamp && (
{_created !== _modified && (
<Text
variant="auxiliary"
sx={{
textAlign: ['left', 'right', 'right'],
}}
>
{'edited ' + formattedModifiedDatestamp}
edited <DisplayDate date={_modified} />
</Text>
)}
</Flex>
Expand Down
Loading

0 comments on commit f34497f

Please sign in to comment.