Skip to content

Commit eeb32a7

Browse files
authored
Convert date cell to single line (#2153)
1 parent 522d3ab commit eeb32a7

File tree

15 files changed

+111
-37
lines changed

15 files changed

+111
-37
lines changed

Diff for: app/components/RefetchIntervalPicker.tsx

+3-2
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,14 @@
66
* Copyright Oxide Computer Company
77
*/
88
import cn from 'classnames'
9-
import { format } from 'date-fns'
109
import { useEffect, useState } from 'react'
1110

1211
import { Refresh16Icon, Time16Icon } from '@oxide/design-system/icons/react'
1312

1413
import { Listbox, type ListboxItem } from '~/ui/lib/Listbox'
1514
import { SpinnerLoader } from '~/ui/lib/Spinner'
1615
import { useInterval } from '~/ui/lib/use-interval'
16+
import { toLocaleTimeString } from '~/util/date'
1717

1818
const intervalPresets = {
1919
Off: undefined,
@@ -55,7 +55,8 @@ export function useIntervalPicker({ enabled, isLoading, fn }: Props) {
5555
intervalPicker: (
5656
<div className="mb-12 flex items-center justify-between">
5757
<div className="hidden items-center gap-2 text-right text-mono-sm text-quaternary lg+:flex">
58-
<Time16Icon className="text-quinary" /> Refreshed {format(lastFetched, 'HH:mm')}
58+
<Time16Icon className="text-quinary" /> Refreshed{' '}
59+
{toLocaleTimeString(lastFetched)}
5960
</div>
6061
<div className="flex">
6162
<button

Diff for: app/components/TimeAgo.tsx

+2-3
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,9 @@
66
* Copyright Oxide Computer Company
77
*/
88
import type { Placement } from '@floating-ui/react'
9-
import { format } from 'date-fns'
109

1110
import { Tooltip } from '~/ui/lib/Tooltip'
12-
import { timeAgoAbbr } from '~/util/date'
11+
import { timeAgoAbbr, toLocaleDateTimeString } from '~/util/date'
1312

1413
export const TimeAgo = ({
1514
datetime,
@@ -23,7 +22,7 @@ export const TimeAgo = ({
2322
const content = (
2423
<div className="flex flex-col">
2524
<span className="text-tertiary">{tooltipText}</span>
26-
<span>{format(datetime, 'MMM d, yyyy p')}</span>
25+
<span>{toLocaleDateTimeString(datetime)}</span>
2726
</div>
2827
)
2928
return (

Diff for: app/forms/disk-create.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
*
66
* Copyright Oxide Computer Company
77
*/
8-
import { format } from 'date-fns'
98
import { filesize } from 'filesize'
109
import { useMemo } from 'react'
1110
import { useController, type Control } from 'react-hook-form'
@@ -35,6 +34,7 @@ import { FormDivider } from '~/ui/lib/Divider'
3534
import { FieldLabel } from '~/ui/lib/FieldLabel'
3635
import { Radio } from '~/ui/lib/Radio'
3736
import { RadioGroup } from '~/ui/lib/RadioGroup'
37+
import { toLocaleDateString } from '~/util/date'
3838
import { bytesToGiB, GiB } from '~/util/units'
3939

4040
const blankDiskSource: DiskSource = {
@@ -258,7 +258,7 @@ const SnapshotSelectField = ({ control }: { control: Control<DiskCreate> }) => {
258258
<>
259259
<div>{i.name}</div>
260260
<div className="text-tertiary selected:text-accent-secondary">
261-
Created on {format(i.timeCreated, 'MMM d, yyyy')}
261+
Created on {toLocaleDateString(i.timeCreated)}
262262
<DiskNameFromId disk={i.diskId} />{' '}
263263
<span className="mx-1 text-quinary selected:text-accent-disabled">/</span>{' '}
264264
{formattedSize.value} {formattedSize.unit}

Diff for: app/forms/idp/edit.tsx

+3-3
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,10 @@ import { NameField } from '~/components/form/fields/NameField'
1515
import { TextField } from '~/components/form/fields/TextField'
1616
import { SideModalForm } from '~/components/form/SideModalForm'
1717
import { getIdpSelector, useForm, useIdpSelector } from '~/hooks'
18+
import { DateTime } from '~/ui/lib/DateTime'
1819
import { PropertiesTable } from '~/ui/lib/PropertiesTable'
1920
import { ResourceLabel } from '~/ui/lib/SideModal'
2021
import { Truncate } from '~/ui/lib/Truncate'
21-
import { formatDateTime } from '~/util/date'
2222
import { pb } from '~/util/path-builder'
2323

2424
EditIdpSideModalForm.loader = async ({ params }: LoaderFunctionArgs) => {
@@ -62,10 +62,10 @@ export function EditIdpSideModalForm() {
6262
<Truncate text={idp.id} maxLength={32} hasCopyButton />
6363
</PropertiesTable.Row>
6464
<PropertiesTable.Row label="Created">
65-
{formatDateTime(idp.timeCreated)}
65+
<DateTime date={idp.timeCreated} />
6666
</PropertiesTable.Row>
6767
<PropertiesTable.Row label="Updated">
68-
{formatDateTime(idp.timeModified)}
68+
<DateTime date={idp.timeModified} />
6969
</PropertiesTable.Row>
7070
</PropertiesTable>
7171

Diff for: app/forms/image-edit.tsx

+3-3
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,10 @@ import {
2121
useProjectImageSelector,
2222
useSiloImageSelector,
2323
} from '~/hooks'
24+
import { DateTime } from '~/ui/lib/DateTime'
2425
import { PropertiesTable } from '~/ui/lib/PropertiesTable'
2526
import { ResourceLabel } from '~/ui/lib/SideModal'
2627
import { Truncate } from '~/ui/lib/Truncate'
27-
import { formatDateTime } from '~/util/date'
2828
import { pb } from '~/util/path-builder'
2929
import { bytesToGiB } from '~/util/units'
3030

@@ -94,10 +94,10 @@ export function EditImageSideModalForm({
9494
<span className="ml-1 inline-block text-quaternary">GiB</span>
9595
</PropertiesTable.Row>
9696
<PropertiesTable.Row label="Created">
97-
{formatDateTime(image.timeCreated)}
97+
<DateTime date={image.timeCreated} />
9898
</PropertiesTable.Row>
9999
<PropertiesTable.Row label="Updated">
100-
{formatDateTime(image.timeModified)}
100+
<DateTime date={image.timeModified} />
101101
</PropertiesTable.Row>
102102
</PropertiesTable>
103103

Diff for: app/pages/project/instances/instance/InstancePage.tsx

+2-7
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
*
66
* Copyright Oxide Computer Company
77
*/
8-
import { format } from 'date-fns'
98
import { filesize } from 'filesize'
109
import { useMemo } from 'react'
1110
import { Link, useNavigate, type LoaderFunctionArgs } from 'react-router-dom'
@@ -25,6 +24,7 @@ import { RouteTabs, Tab } from '~/components/RouteTabs'
2524
import { InstanceStatusBadge } from '~/components/StatusBadge'
2625
import { getInstanceSelector, useInstanceSelector, useQuickActions } from '~/hooks'
2726
import { EmptyCell } from '~/table/cells/EmptyCell'
27+
import { DateTime } from '~/ui/lib/DateTime'
2828
import { PageHeader, PageTitle } from '~/ui/lib/PageHeader'
2929
import { PropertiesTable } from '~/ui/lib/PropertiesTable'
3030
import { Truncate } from '~/ui/lib/Truncate'
@@ -172,12 +172,7 @@ export function InstancePage() {
172172
</span>
173173
</PropertiesTable.Row>
174174
<PropertiesTable.Row label="created">
175-
<span className="text-secondary">
176-
{format(instance.timeCreated, 'MMM d, yyyy')}{' '}
177-
</span>
178-
<span className="ml-1 text-quaternary">
179-
{format(instance.timeCreated, 'p')}
180-
</span>
175+
<DateTime date={instance.timeCreated} />
181176
</PropertiesTable.Row>
182177
<PropertiesTable.Row label="id">
183178
<span className="overflow-hidden text-ellipsis whitespace-nowrap text-secondary">

Diff for: app/pages/project/vpcs/VpcPage/VpcPage.tsx

+3-3
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,10 @@ import { Networking24Icon } from '@oxide/design-system/icons/react'
1313
import { QueryParamTabs } from '~/components/QueryParamTabs'
1414
import { getVpcSelector, useVpcSelector } from '~/hooks'
1515
import { EmptyCell } from '~/table/cells/EmptyCell'
16+
import { DateTime } from '~/ui/lib/DateTime'
1617
import { PageHeader, PageTitle } from '~/ui/lib/PageHeader'
1718
import { PropertiesTable } from '~/ui/lib/PropertiesTable'
1819
import { Tabs } from '~/ui/lib/Tabs'
19-
import { formatDateTime } from '~/util/date'
2020

2121
import { VpcFirewallRulesTab } from './tabs/VpcFirewallRulesTab'
2222
import { VpcSubnetsTab } from './tabs/VpcSubnetsTab'
@@ -56,10 +56,10 @@ export function VpcPage() {
5656
</PropertiesTable>
5757
<PropertiesTable>
5858
<PropertiesTable.Row label="Created">
59-
{formatDateTime(vpc.timeCreated)}
59+
<DateTime date={vpc.timeCreated} />
6060
</PropertiesTable.Row>
6161
<PropertiesTable.Row label="Last Modified">
62-
{formatDateTime(vpc.timeModified)}
62+
<DateTime date={vpc.timeModified} />
6363
</PropertiesTable.Row>
6464
</PropertiesTable>
6565
</PropertiesTable.Group>

Diff for: app/pages/system/silos/SiloPage.tsx

+3-3
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,12 @@ import { QueryParamTabs } from '~/components/QueryParamTabs'
1414
import { getSiloSelector, useSiloSelector } from '~/hooks'
1515
import { EmptyCell } from '~/table/cells/EmptyCell'
1616
import { Badge } from '~/ui/lib/Badge'
17+
import { DateTime } from '~/ui/lib/DateTime'
1718
import { EmptyMessage } from '~/ui/lib/EmptyMessage'
1819
import { PageHeader, PageTitle } from '~/ui/lib/PageHeader'
1920
import { PropertiesTable } from '~/ui/lib/PropertiesTable'
2021
import { TableEmptyBox } from '~/ui/lib/Table'
2122
import { Tabs } from '~/ui/lib/Tabs'
22-
import { formatDateTime } from '~/util/date'
2323

2424
import { SiloIdpsTab } from './SiloIdpsTab'
2525
import { SiloIpPoolsTab } from './SiloIpPoolsTab'
@@ -64,10 +64,10 @@ export function SiloPage() {
6464
</PropertiesTable>
6565
<PropertiesTable>
6666
<PropertiesTable.Row label="Created">
67-
{formatDateTime(silo.timeCreated)}
67+
<DateTime date={silo.timeCreated} />
6868
</PropertiesTable.Row>
6969
<PropertiesTable.Row label="Last Modified">
70-
{formatDateTime(silo.timeModified)}
70+
<DateTime date={silo.timeModified} />
7171
</PropertiesTable.Row>
7272
</PropertiesTable>
7373
</PropertiesTable.Group>

Diff for: app/table/columns/common.tsx

+2-4
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,19 @@
66
* Copyright Oxide Computer Company
77
*/
88

9-
import { format } from 'date-fns/format'
109
import { filesize } from 'filesize'
1110

11+
import { DateTime } from '~/ui/lib/DateTime'
1212
import { Truncate } from '~/ui/lib/Truncate'
1313

1414
import { EmptyCell } from '../cells/EmptyCell'
15-
import { TwoLineCell } from '../cells/TwoLineCell'
1615

1716
// the full type of the info arg is CellContext<Row, Item> from RT, but in these
1817
// cells we only care about the return value of getValue
1918
type Info<T> = { getValue: () => T }
2019

2120
function dateCell(info: Info<Date>) {
22-
const date = info.getValue()
23-
return <TwoLineCell value={[format(date, 'MMM d, yyyy'), format(date, 'p')]} />
21+
return <DateTime date={info.getValue()} />
2422
}
2523

2624
function sizeCell(info: Info<number>) {

Diff for: app/ui/lib/DateTime.tsx

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
/*
2+
* This Source Code Form is subject to the terms of the Mozilla Public
3+
* License, v. 2.0. If a copy of the MPL was not distributed with this
4+
* file, you can obtain one at https://mozilla.org/MPL/2.0/.
5+
*
6+
* Copyright Oxide Computer Company
7+
*/
8+
9+
import { toLocaleDateString, toLocaleTimeString } from '~/util/date'
10+
11+
export const DateTime = ({ date, locale }: { date: Date; locale?: string }) => (
12+
<time dateTime={date.toISOString()} className="flex flex-wrap gap-x-2">
13+
<span>{toLocaleDateString(date, locale)}</span>
14+
<span className="text-quaternary">{toLocaleTimeString(date, locale)}</span>
15+
</time>
16+
)

Diff for: app/util/date.spec.ts

+30-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,12 @@
88
import { subDays, subHours, subMinutes, subSeconds } from 'date-fns'
99
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
1010

11-
import { timeAgoAbbr } from './date'
11+
import {
12+
timeAgoAbbr,
13+
toLocaleDateString,
14+
toLocaleDateTimeString,
15+
toLocaleTimeString,
16+
} from './date'
1217

1318
const baseDate = new Date(2021, 5, 7)
1419

@@ -54,4 +59,28 @@ describe('timeAgoAbbr', () => {
5459
expect(timeAgoAbbr(subDays(baseDate, 200), { addSuffix: true })).toEqual('7mo ago')
5560
expect(timeAgoAbbr(subDays(baseDate, 3), { addSuffix: true })).toEqual('3d ago')
5661
})
62+
63+
it('formats toLocaleDateString', () => {
64+
expect(toLocaleDateString(baseDate)).toEqual('Jun 7, 2021')
65+
expect(toLocaleDateString(baseDate, 'en-US')).toEqual('Jun 7, 2021')
66+
expect(toLocaleDateString(baseDate, 'fr-FR')).toEqual('7 juin 2021')
67+
expect(toLocaleDateString(baseDate, 'de-DE')).toEqual('07.06.2021')
68+
expect(toLocaleDateString(baseDate, 'ja-JP')).toEqual('2021/06/07')
69+
})
70+
71+
it('formats toLocaleTimeString', () => {
72+
expect(toLocaleTimeString(baseDate)).toEqual('12:00 AM')
73+
expect(toLocaleTimeString(baseDate, 'en-US')).toEqual('12:00 AM')
74+
expect(toLocaleTimeString(baseDate, 'fr-FR')).toEqual('00:00')
75+
expect(toLocaleTimeString(baseDate, 'de-DE')).toEqual('00:00')
76+
expect(toLocaleTimeString(baseDate, 'ja-JP')).toEqual('0:00')
77+
})
78+
79+
it('formats toLocaleDateTimeString', () => {
80+
expect(toLocaleDateTimeString(baseDate)).toEqual('Jun 7, 2021, 12:00 AM')
81+
expect(toLocaleDateTimeString(baseDate, 'en-US')).toEqual('Jun 7, 2021, 12:00 AM')
82+
expect(toLocaleDateTimeString(baseDate, 'fr-FR')).toEqual('7 juin 2021, 00:00')
83+
expect(toLocaleDateTimeString(baseDate, 'de-DE')).toEqual('07.06.2021, 00:00')
84+
expect(toLocaleDateTimeString(baseDate, 'ja-JP')).toEqual('2021/06/07 0:00')
85+
})
5786
})

Diff for: app/util/date.ts

+11-6
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,7 @@
55
*
66
* Copyright Oxide Computer Company
77
*/
8-
import {
9-
format,
10-
formatDistanceToNowStrict,
11-
type FormatDistanceToNowStrictOptions,
12-
} from 'date-fns'
8+
import { formatDistanceToNowStrict, type FormatDistanceToNowStrictOptions } from 'date-fns'
139

1410
// locale setup and formatDistance function copied from here and modified
1511
// https://github.com/date-fns/date-fns/blob/56a3856/src/locale/en-US/_lib/formatDistance/index.js
@@ -47,4 +43,13 @@ export const timeAgoAbbr = (d: Date, options?: FormatDistanceToNowStrictOptions)
4743
},
4844
})
4945

50-
export const formatDateTime = (d: Date) => format(d, 'MMM d, yyyy H:mm aa')
46+
// dateStyle: 'medium' looks like `Apr 16, 2024` for en-US
47+
export const toLocaleDateString = (d: Date, locale?: string) =>
48+
new Intl.DateTimeFormat(locale, { dateStyle: 'medium' }).format(d)
49+
50+
// timeStyle: 'short' looks like `8:33 PM` for en-US
51+
export const toLocaleTimeString = (d: Date, locale?: string) =>
52+
new Intl.DateTimeFormat(locale, { timeStyle: 'short' }).format(d)
53+
54+
export const toLocaleDateTimeString = (d: Date, locale?: string) =>
55+
new Intl.DateTimeFormat(locale, { dateStyle: 'medium', timeStyle: 'short' }).format(d)

Diff for: test/e2e/dates.e2e.ts

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/*
2+
* This Source Code Form is subject to the terms of the Mozilla Public
3+
* License, v. 2.0. If a copy of the MPL was not distributed with this
4+
* file, you can obtain one at https://mozilla.org/MPL/2.0/.
5+
*
6+
* Copyright Oxide Computer Company
7+
*/
8+
import { expect, test } from '@playwright/test'
9+
10+
test('date formatting - English locale', async ({ page }) => {
11+
await page.goto('/system/silos')
12+
await expect(page.getByText('Feb 28, 202312:00 AM')).toBeVisible()
13+
})
14+
15+
test.describe('date formatting - German locale', () => {
16+
test.use({ locale: 'de-DE' })
17+
test('date formatting - German locale', async ({ page }) => {
18+
await page.goto('/system/silos')
19+
await expect(page.getByText('28.02.202300:00')).toBeVisible()
20+
})
21+
})
22+
23+
test.describe('date formatting - French locale', () => {
24+
test.use({ locale: 'fr-FR' })
25+
test('date formatting - French locale', async ({ page }) => {
26+
await page.goto('/system/silos')
27+
await expect(page.getByText('28 févr. 202300:00')).toBeVisible()
28+
})
29+
})

Diff for: test/e2e/silos.e2e.ts

+1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ test('Create silo', async ({ page }) => {
2828
// not easy to assert this until we can calculate accessible name instead of text content
2929
// discoverable: 'true',
3030
})
31+
await expect(page.getByText('Feb 28, 202312:00 AM')).toBeVisible()
3132

3233
await page.click('role=link[name="New silo"]')
3334

Diff for: test/e2e/vpcs.e2e.ts

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ test('can nav to VpcPage from /', async ({ page }) => {
1212
await page.getByRole('table').getByRole('link', { name: 'mock-project' }).click()
1313
await page.getByRole('link', { name: 'VPCs' }).click()
1414
await page.getByRole('link', { name: 'mock-vpc' }).click()
15+
await expect(page.getByText('Jan 1, 202112:00 AM')).toBeVisible()
1516
await expect(page.getByRole('tab', { name: 'Firewall rules' })).toBeVisible()
1617
await expect(page.getByRole('cell', { name: 'allow-icmp' })).toBeVisible()
1718
expect(await page.title()).toEqual('mock-vpc / VPCs / mock-project / Oxide Console')

0 commit comments

Comments
 (0)