Skip to content

Commit

Permalink
Merge pull request #2414 from Vignesh-M21/fix/date-browser-compatible
Browse files Browse the repository at this point in the history
Fix date browser compatible
  • Loading branch information
paales authored Nov 15, 2024
2 parents 0c7d5ea + cd8e35b commit 19b7486
Show file tree
Hide file tree
Showing 11 changed files with 132 additions and 15 deletions.
5 changes: 5 additions & 0 deletions .changeset/small-pans-cry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@graphcommerce/next-ui': patch
---

Added RelativeTimeFormat and RelativeToTimeFormat components to display relative dates in all locales.
6 changes: 6 additions & 0 deletions .changeset/sweet-insects-joke.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@graphcommerce/magento-graphcms': patch
'@graphcommerce/next-ui': patch
---

Date strings (12-12-2012) are not supported by older Safari browser versions. must be converted (12/12/2012) in order for it to function; otherwise, it will return NaN if we attempt to access the getTime() on an object.
10 changes: 4 additions & 6 deletions examples/magento-graphcms/pages/account/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,10 @@ import {
iconPerson,
iconShutdown,
iconStar,
TimeAgo,
LayoutTitle,
LayoutHeader,
iconBin,
RelativeToTimeFormat,
} from '@graphcommerce/next-ui'
import { i18n } from '@lingui/core'
import { Trans } from '@lingui/react'
Expand All @@ -51,8 +51,6 @@ function AccountIndexPage() {
const orders = customer?.orders
const latestOrder = orders?.items?.[(orders?.items?.length ?? 1) - 1]

const latestOrderDate = new Date(latestOrder?.order_date ?? new Date())

return (
<>
<PageMeta title={i18n._(/* i18n */ 'Account')} metaRobots={['noindex']} />
Expand Down Expand Up @@ -95,9 +93,9 @@ function AccountIndexPage() {
subtitle={
latestOrder ? (
<>
<time dateTime={latestOrderDate.toDateString()}>
<TimeAgo date={latestOrderDate} locale={locale} />
</time>
<RelativeToTimeFormat styleFormat='short'>
{latestOrder?.order_date}
</RelativeToTimeFormat>
{', '}
{latestOrder?.items && <OrderStateLabelInline {...latestOrder} />}
</>
Expand Down
11 changes: 3 additions & 8 deletions packages/next-ui/Intl/DateTimeFormat/DateTimeFormat.tsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,20 @@
import { useMemo } from 'react'
import { useLocale } from '../../hooks/useLocale'
import { useMemoObject } from '../../hooks/useMemoObject'
import { DateValue, toDate } from './toDate'

export function useDateTimeFormatter(props: Intl.DateTimeFormatOptions) {
const locale = useLocale()
const memoOptions = useMemoObject(props)
return useMemo(() => new Intl.DateTimeFormat(locale, memoOptions), [locale, memoOptions])
}

type DateValue = Date | string | number | null | undefined
export type DateTimeFormatPropsType = { children: DateValue } & Intl.DateTimeFormatOptions

export function DateTimeFormat(props: DateTimeFormatPropsType) {
const { children } = props
const formatter = useDateTimeFormatter({ dateStyle: 'medium', timeStyle: 'short', ...props })

return (
<span suppressHydrationWarning>
{children
? formatter.format(typeof children === 'string' ? new Date(children) : children)
: null}
</span>
)
const dateValue = useMemo(() => toDate(children), [children])
return <span suppressHydrationWarning>{dateValue ? formatter.format(dateValue) : null}</span>
}
16 changes: 16 additions & 0 deletions packages/next-ui/Intl/DateTimeFormat/toDate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
export type DateValue = Date | string | number | null | undefined

export function toDate(value: DateValue): Date | undefined {
let date: Date | undefined

if (value instanceof Date) {
date = value
} else if (typeof value === 'string') {
date = new Date(value.replace(/-/g, '/'))
} else if (typeof value === 'number') {
date = new Date(value)
}

if (date && Number.isNaN(date.getTime())) return undefined
return date
}
34 changes: 34 additions & 0 deletions packages/next-ui/Intl/RelativeTimeFormat/RelativeTimeFormat.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { forwardRef, useMemo } from 'react'
import { useLocale } from '../../hooks/useLocale'
import { useMemoObject } from '../../hooks/useMemoObject'
import { relativeTimeFormatUnitAuto } from './relativeTimeFormatAutoUnit'

export function useRelativeTimeFormatter(props: Intl.RelativeTimeFormatOptions) {
const locale = useLocale()
const memoOptions = useMemoObject(props)
return useMemo(() => new Intl.RelativeTimeFormat(locale, memoOptions), [locale, memoOptions])
}

export type RelativeTimeFormatProps = {
children: number
unit?: Intl.RelativeTimeFormatUnit
styleFormat?: Intl.RelativeTimeFormatStyle
} & Omit<Intl.RelativeTimeFormatOptions, 'style'>

/**
* Alternative: {@link file://./RelativeToTimeFormat.tsx}
*/
export const RelativeTimeFormat = forwardRef<HTMLSpanElement, RelativeTimeFormatProps>(
(props, ref) => {
const { children, unit, styleFormat, localeMatcher, numeric, ...rest } = props
const formatter = useRelativeTimeFormatter({ localeMatcher, numeric, style: styleFormat })

const [value, autoUnit] = relativeTimeFormatUnitAuto({ value: children, unit })

return (
<span suppressHydrationWarning ref={ref} {...rest}>
{children ? formatter.format(value, autoUnit) : null}
</span>
)
},
)
35 changes: 35 additions & 0 deletions packages/next-ui/Intl/RelativeTimeFormat/RelativeToTimeFormat.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { forwardRef, useMemo } from 'react'
import { DateValue, toDate } from '../DateTimeFormat/toDate'
import { RelativeTimeFormat, RelativeTimeFormatProps } from './RelativeTimeFormat'

type RelativeToTimeFormatProps = Omit<RelativeTimeFormatProps, 'children'> & {
/**
* Date to format a relative value for.
*/
children: DateValue
/**
* If provided, the component will format a relative value to this date.
* Else, it will format a relative value to the current date.
*/
to?: DateValue
}

export const RelativeToTimeFormat = forwardRef<HTMLSpanElement, RelativeToTimeFormatProps>(
(props, ref) => {
const { children, to, ...rest } = props

const relativeTo = useMemo(() => {
const date = toDate(children)
if (!date) return 0
const toDateValue = (to && toDate(to)) || new Date()

return Math.round((date.getTime() - toDateValue.getTime()) / 1000)
}, [children, to])

return (
<RelativeTimeFormat {...rest} ref={ref}>
{relativeTo}
</RelativeTimeFormat>
)
},
)
2 changes: 2 additions & 0 deletions packages/next-ui/Intl/RelativeTimeFormat/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './RelativeTimeFormat'
export * from './RelativeToTimeFormat'
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
type UseRelativeTimeFormatUnitAutoProps = {
value: number
unit?: Intl.RelativeTimeFormatUnit
}

export function relativeTimeFormatUnitAuto(
props: UseRelativeTimeFormatUnitAutoProps,
): [number, Intl.RelativeTimeFormatUnit] {
const { value, unit } = props

if (unit) return [value, unit]

// Calculate the absolute value once
const absValue = Math.abs(value)

if (absValue >= 60 * 60 * 24 * 365) return [Math.round(value / (60 * 60 * 24 * 365)), 'year']
if (absValue >= 60 * 60 * 24 * 30) return [Math.round(value / (60 * 60 * 24 * 30)), 'month']
if (absValue >= 60 * 60 * 24 * 7) return [Math.round(value / (60 * 60 * 24 * 7)), 'week']
if (absValue >= 60 * 60 * 24) return [Math.round(value / (60 * 60 * 24)), 'day']
if (absValue >= 60 * 60) return [Math.round(value / (60 * 60)), 'hour']
if (absValue >= 60) return [Math.round(value / 60), 'minute']
return [Math.round(value), 'second']
}
2 changes: 1 addition & 1 deletion packages/next-ui/Intl/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@ export * from './ListFormat' // Intl.ListFormat
// export * from './Locale' // Intl.Locale
export * from './NumberFormat' // Intl.NumberFormat
// export * from './PluralRules' // Intl.PluralRules
// export * from './RelativeTimeFormat' // Intl.RelativeTimeFormat
export * from './RelativeTimeFormat' // Intl.RelativeTimeFormat
// export * from './Segmenter' // Intl.Segmenter
3 changes: 3 additions & 0 deletions packages/next-ui/TimeAgo/TimeAgo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ export type TimeAgoProps = {
locale?: string
}

/**
* @deprecated Use <RelativeToTimeFormat /> instead.
*/
export function TimeAgo(props: TimeAgoProps) {
const { date } = props
const msPerMinute = 60 * 1000
Expand Down

0 comments on commit 19b7486

Please sign in to comment.