diff --git a/src/components/DateTime/DateTime.stories.tsx b/src/components/DateDetails/DateDetails.stories.tsx similarity index 86% rename from src/components/DateTime/DateTime.stories.tsx rename to src/components/DateDetails/DateDetails.stories.tsx index 71331f36..c2cc647d 100644 --- a/src/components/DateTime/DateTime.stories.tsx +++ b/src/components/DateDetails/DateDetails.stories.tsx @@ -1,4 +1,4 @@ -import { DateTime } from "./DateTime"; +import { DateDetails } from "./DateDetails"; export default { argTypes: { @@ -31,8 +31,8 @@ export default { }, }, }, - component: DateTime, - title: "Display/DateTime", + component: DateDetails, + title: "Display/DateDetails", tags: ["autodocs"], }; @@ -42,6 +42,6 @@ export const Playground = { locale: "en-US", side: "top", systemTimeZone: "America/Los_Angeles", - title: "DateTime", + title: "DateDetails", }, }; diff --git a/src/components/DateDetails/DateDetails.test.tsx b/src/components/DateDetails/DateDetails.test.tsx new file mode 100644 index 00000000..7371b863 --- /dev/null +++ b/src/components/DateDetails/DateDetails.test.tsx @@ -0,0 +1,95 @@ +import { DateDetails } from "@/components/DateDetails/DateDetails"; +import { renderCUI } from "@/utils/test-utils"; +import { fireEvent } from "@testing-library/dom"; + +describe("DateDetails", () => { + const actualTZ = process.env.TZ; + + beforeAll(() => { + global.ResizeObserver = vi.fn(() => { + return { + observe: vi.fn(), + unobserve: vi.fn(), + disconnect: vi.fn(), + }; + }); + + process.env.TZ = "America/New_York"; + }); + + afterAll(() => { + process.env.TZ = actualTZ; + }); + + it("renders the DateDetails component with relevant timezone information", () => { + const baseDate = new Date("2024-12-24 11:45:00 AM"); + const systemTimeZone = "America/Los_Angeles"; + const locale = new Intl.Locale("en", { region: "US" }); + vi.setSystemTime(baseDate); + + const fiveMinutesAgo = new Date("2024-12-24 11:40:00 AM"); + + const { getByText } = renderCUI( + + ); + + const trigger = getByText("5 minutes ago"); + expect(trigger).toBeInTheDocument(); + + fireEvent.click(trigger); + expect( + getByText(content => { + return content.includes("EST"); + }) + ).toBeInTheDocument(); + expect( + getByText(content => { + return content.includes("PST"); + }) + ).toBeInTheDocument(); + expect(getByText("Dec 24, 2024, 4:40:00 PM")).toBeInTheDocument(); + expect(getByText("Dec 24, 2024, 11:40:00 AM (EST)")).toBeInTheDocument(); + expect(getByText("Dec 24, 2024, 8:40:00 AM (PST)")).toBeInTheDocument(); + expect(getByText(fiveMinutesAgo.getTime() / 1000)).toBeInTheDocument(); + }); + + it("handles Daylight Savings Time", () => { + const baseDate = new Date("2024-07-04 11:45:00 AM"); + const systemTimeZone = "America/Los_Angeles"; + const locale = new Intl.Locale("en", { region: "US" }); + vi.setSystemTime(baseDate); + + const fiveMinutesAgo = new Date("2024-07-04 11:40:00 AM"); + + const { getByText } = renderCUI( + + ); + + const trigger = getByText("5 minutes ago"); + expect(trigger).toBeInTheDocument(); + + fireEvent.click(trigger); + expect( + getByText(content => { + return content.includes("EDT"); + }) + ).toBeInTheDocument(); + expect( + getByText(content => { + return content.includes("PDT"); + }) + ).toBeInTheDocument(); + expect(getByText("Jul 4, 2024, 3:40:00 PM")).toBeInTheDocument(); + expect(getByText("Jul 4, 2024, 11:40:00 AM (EDT)")).toBeInTheDocument(); + expect(getByText("Jul 4, 2024, 8:40:00 AM (PDT)")).toBeInTheDocument(); + expect(getByText(fiveMinutesAgo.getTime() / 1000)).toBeInTheDocument(); + }); +}); diff --git a/src/components/DateTime/DateTime.tsx b/src/components/DateDetails/DateDetails.tsx similarity index 81% rename from src/components/DateTime/DateTime.tsx rename to src/components/DateDetails/DateDetails.tsx index 7de5a31b..dce863ea 100644 --- a/src/components/DateTime/DateTime.tsx +++ b/src/components/DateDetails/DateDetails.tsx @@ -65,17 +65,17 @@ const UnderlinedTrigger = styled(Popover.Trigger)` const dateStyle = "medium"; const timeStyle = "medium"; -const createBasicDateTimeFormatter = () => { +const createBasicDateDetailsFormatter = () => { return new Intl.DateTimeFormat(undefined, { dateStyle, timeStyle, }); }; -const formatDateTime = (date: Date, locale?: Intl.Locale, timeZone?: string) => { - let dateTimeFormatter; +const formatDateDetails = (date: Date, locale?: Intl.Locale, timeZone?: string) => { + let dateDetailsFormatter; try { - dateTimeFormatter = new Intl.DateTimeFormat(locale, { + dateDetailsFormatter = new Intl.DateTimeFormat(locale, { dateStyle, timeStyle, timeZone, @@ -83,46 +83,46 @@ const formatDateTime = (date: Date, locale?: Intl.Locale, timeZone?: string) => } catch (error) { if ((error as Error).message.includes("invalid time zone")) { try { - dateTimeFormatter = new Intl.DateTimeFormat(locale, { + dateDetailsFormatter = new Intl.DateTimeFormat(locale, { dateStyle, timeStyle, }); } catch { - dateTimeFormatter = createBasicDateTimeFormatter(); + dateDetailsFormatter = createBasicDateDetailsFormatter(); } } else if ((error as Error).message.includes("invalid language tag")) { try { - dateTimeFormatter = new Intl.DateTimeFormat(undefined, { + dateDetailsFormatter = new Intl.DateTimeFormat(undefined, { dateStyle, timeStyle, timeZone, }); } catch { - dateTimeFormatter = createBasicDateTimeFormatter(); + dateDetailsFormatter = createBasicDateDetailsFormatter(); } } else { - dateTimeFormatter = createBasicDateTimeFormatter(); + dateDetailsFormatter = createBasicDateDetailsFormatter(); } } - return dateTimeFormatter.format(date); + return dateDetailsFormatter.format(date); }; export type ArrowPosition = "top" | "right" | "left" | "bottom"; -export interface DateTimeProps { +export interface DateDetailsProps { date: Date; locale?: Intl.Locale; side?: ArrowPosition; systemTimeZone?: string; } -export const DateTime = ({ +export const DateDetails = ({ date, locale, side = "top", systemTimeZone, -}: DateTimeProps) => { +}: DateDetailsProps) => { const dayjsDate = dayjs(date); let systemTime; @@ -155,7 +155,7 @@ export const DateTime = ({ Local - {formatDateTime(dayjsDate.toDate(), locale)} ({dayjsDate.format("z")}) + {formatDateDetails(dayjsDate.toDate(), locale)} ({dayjsDate.format("z")}) @@ -165,7 +165,7 @@ export const DateTime = ({ - {formatDateTime(systemTime.toDate(), locale, systemTimeZone)} ( + {formatDateDetails(systemTime.toDate(), locale, systemTimeZone)} ( {systemTime.format("z")}) @@ -175,7 +175,7 @@ export const DateTime = ({ UTC - {formatDateTime(dayjsDate.utc().toDate(), locale, "UTC")} + {formatDateDetails(dayjsDate.utc().toDate(), locale, "UTC")} diff --git a/src/components/DateTime/DateTime.test.tsx b/src/components/DateTime/DateTime.test.tsx deleted file mode 100644 index 669f5c79..00000000 --- a/src/components/DateTime/DateTime.test.tsx +++ /dev/null @@ -1,60 +0,0 @@ -import { DateTime } from "@/components/DateTime/DateTime"; -import { renderCUI } from "@/utils/test-utils"; -import { fireEvent } from "@testing-library/dom"; - -describe("DateTime", () => { - const actualTZ = process.env.TZ; - - beforeAll(() => { - global.ResizeObserver = vi.fn(() => { - return { - observe: vi.fn(), - unobserve: vi.fn(), - disconnect: vi.fn(), - }; - }); - - process.env.TZ = "America/New_York"; - }); - - afterAll(() => { - process.env.TZ = actualTZ; - }); - - it("renders the DateTime component with relevant timezone information", () => { - const baseDate = new Date("2024-07-04 11:45:00 AM"); - const systemTimeZone = "America/Los_Angeles"; - const locale = new Intl.Locale("en", { region: "US" }); - vi.setSystemTime(baseDate); - - const fiveMinutesAgo = new Date("2024-07-04 11:40:00 AM"); - - const { getByText } = renderCUI( - - ); - - const trigger = getByText("5 minutes ago"); - expect(trigger).toBeInTheDocument(); - - fireEvent.click(trigger); - expect( - getByText(content => { - return content.startsWith("Local (America/New_York"); - }) - ).toBeInTheDocument(); - expect( - getByText(content => { - return content.startsWith("System (America/Los_Angeles)"); - }) - ).toBeInTheDocument(); - expect(getByText("Jul 4, 2024, 3:40:00 PM")).toBeInTheDocument(); - expect(getByText("Jul 4, 2024, 11:40:00 AM")).toBeInTheDocument(); - expect(getByText("Jul 4, 2024, 8:40:00 AM")).toBeInTheDocument(); - expect(getByText(fiveMinutesAgo.getTime() / 1000)).toBeInTheDocument(); - expect(getByText(fiveMinutesAgo.toISOString())).toBeInTheDocument(); - }); -}); diff --git a/src/components/index.ts b/src/components/index.ts index 15fb55e1..6f4ecf98 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -23,8 +23,8 @@ export { CodeBlock } from "./CodeBlock/CodeBlock"; export { ConfirmationDialog } from "./ConfirmationDialog/ConfirmationDialog"; export { ContextMenu } from "./ContextMenu/ContextMenu"; export { Container } from "./Container/Container"; +export { DateDetails } from "@/components/DateDetails/DateDetails"; export { DatePicker } from "./DatePicker/DatePicker"; -export { DateTime } from "@/components/DateTime/DateTime"; export { Dialog } from "./Dialog/Dialog"; export { EllipsisContent } from "./EllipsisContent/EllipsisContent"; export { Flyout } from "./Flyout/Flyout";