-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #149 from 2wheeh/feature/issue-127/time
[#127] Add Time component
- Loading branch information
Showing
8 changed files
with
123 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
export default function globalSetup() { | ||
process.env.TZ = 'Asia/Seoul'; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
'use client'; | ||
|
||
import { type Locale, formatDateToLocal } from '@/lib/utils/format-date-to-local'; | ||
import Text, { type TextProps } from '@/components/atomic/text'; | ||
|
||
interface Props extends Omit<TextProps, 'children'> { | ||
dateStr: string; | ||
locale?: Locale; | ||
relative?: boolean; | ||
} | ||
|
||
export default function ClientTime({ | ||
dateStr, | ||
locale = 'ko-KR', | ||
relative = false, | ||
...textProps | ||
}: Props) { | ||
return <Text {...textProps}>{formatDateToLocal(dateStr, locale, relative)}</Text>; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
import dynamic from 'next/dynamic'; | ||
const Time = dynamic(() => import('@/components/time/client-time'), { ssr: false }); | ||
|
||
export default Time; |
51 changes: 51 additions & 0 deletions
51
ui/packages/mr-c.app/src/lib/utils/format-date-to-local.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
import { formatDateToLocal } from '@/lib/utils/format-date-to-local'; | ||
|
||
function getRelativeDateStr(interval: number) { | ||
const currentDateTime = new Date().getTime(); | ||
return new Date(currentDateTime - interval).toISOString(); | ||
} | ||
|
||
describe('formatDateToLocal', () => { | ||
describe('default mode', () => { | ||
const certainDateStr = '2023-04-02T15:08:00+09:00'; | ||
|
||
test('should format time in Korean', () => { | ||
expect(formatDateToLocal(certainDateStr, 'ko-KR')).toBe('2023년 4월 2일'); | ||
}); | ||
|
||
test('should format time in English', () => { | ||
expect(formatDateToLocal(certainDateStr, 'en-US')).toBe('Apr 2, 2023'); | ||
}); | ||
}); | ||
|
||
describe('relative mode', () => { | ||
let relativeDateStr: string; | ||
|
||
test.each([ | ||
{ interval: 1000 * 5, expected: '몇 초 전' }, | ||
{ interval: 1000 * 89, expected: '1분 전' }, | ||
{ interval: 1000 * 91, expected: '2분 전' }, | ||
{ interval: 1000 * 60 * 60 * 24 * 3, expected: '3일 전' }, | ||
{ interval: -1 * 1000 * 90, expected: '2분 후' }, | ||
])('should format time in Korean', ({ interval, expected }) => { | ||
relativeDateStr = getRelativeDateStr(interval); | ||
expect(formatDateToLocal(relativeDateStr, 'ko-KR', true)).toBe(expected); | ||
}); | ||
|
||
test.each([ | ||
{ interval: 1000 * 5, expected: 'a few seconds ago' }, | ||
{ interval: 1000 * 89, expected: 'a minute ago' }, | ||
{ interval: 1000 * 91, expected: '2 minutes ago' }, | ||
{ interval: 1000 * 60 * 60 * 24 * 3, expected: '3 days ago' }, | ||
{ interval: -1 * 1000 * 90, expected: 'in 2 minutes' }, | ||
])('should format time in English', ({ interval, expected }) => { | ||
relativeDateStr = getRelativeDateStr(interval); | ||
expect(formatDateToLocal(relativeDateStr, 'en-US', true)).toBe(expected); | ||
}); | ||
}); | ||
|
||
test('should throw an Error when date string is not valid', () => { | ||
const invalidDateStr = 'foo'; | ||
expect(() => formatDateToLocal(invalidDateStr, 'en-US')).toThrowError(invalidDateStr); | ||
}); | ||
}); |
35 changes: 35 additions & 0 deletions
35
ui/packages/mr-c.app/src/lib/utils/format-date-to-local.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
import dayjs from 'dayjs'; | ||
import relativeTime from 'dayjs/plugin/relativeTime'; | ||
import 'dayjs/locale/ko'; | ||
|
||
dayjs.extend(relativeTime); | ||
|
||
function isInvalid(dateStr: string) { | ||
return isNaN(Date.parse(dateStr)); | ||
} | ||
|
||
export type Locale = 'ko-KR' | 'en-US'; | ||
|
||
// If this function is called on server, it would reflect | ||
// the timezone where the server at rather than the client at. | ||
// This could cause a react hydration error. | ||
export function formatDateToLocal(dateStr: string, locale: Locale, relative = false) { | ||
if (isInvalid(dateStr)) { | ||
throw new Error(`${dateStr} is not a valid date string`); | ||
} | ||
|
||
const date = new Date(dateStr); | ||
|
||
if (relative) { | ||
return dayjs(date).locale(locale).fromNow(); | ||
} | ||
|
||
const options: Intl.DateTimeFormatOptions = { | ||
day: 'numeric', | ||
month: 'short', | ||
year: 'numeric', | ||
}; | ||
const formatter = new Intl.DateTimeFormat(locale, options); | ||
|
||
return formatter.format(date); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters