Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/ui-date-segment-locale-separator.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@medusajs/ui": patch
---

fix(ui): keep the gap between date and time segments in locales whose separator is a space
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { render } from "@testing-library/react"
import * as React from "react"
import { I18nProvider } from "react-aria"

import { DatePicker } from "../date-picker"

describe("DateSegment", () => {
it.each(["en-US", "fr-FR"])(
"renders every date/time separator as a spacing span in %s",
(locale) => {
const { container } = render(
<I18nProvider locale={locale}>
<DatePicker
granularity="minute"
value={new Date(2026, 8, 9, 9, 5)}
aria-label="Date"
/>
</I18nProvider>
)

/**
* The literal between the date and the time depends on the locale:
* ", " in en-US, a lone " " in fr-FR. Both must become the spacing
* span. A ", " left as text is a visible comma without a gap; a " "
* left as text collapses and glues the date to the time.
*
* Node's ICU also emits a " " before the day period in en-US
* (09:05 AM), so the number of spans is not asserted: what matters is
* that no separator survives as text.
*/
expect(container.querySelector("span.mx-1")).not.toBeNull()

const leafTexts = Array.from(container.querySelectorAll("div"))
.filter((element) => element.children.length === 0)
.map((element) => element.textContent ?? "")

expect(leafTexts).not.toContain(", ")
expect(
leafTexts.filter((text) => text.length > 0 && text.trim() === "")
).toHaveLength(0)
}
)
})
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,18 @@ const DateSegment = ({ segment, state }: DateSegmentProps) => {
const ref = React.useRef<HTMLDivElement>(null)
const { segmentProps } = useDateSegment(segment, state, ref)

const isComma = segment.type === "literal" && segment.text === ", "
const isSpacingLiteral =
segment.type === "literal" &&
(segment.text === ", " || segment.text.trim() === "")

/**
* We render an empty span with a margin to maintain the correct spacing
* between date and time segments.
* between date and time segments. The literal between them depends on the
* locale: ", " in en-US, but a lone " " in fr-FR and many others. A text
* node made only of whitespace collapses to nothing at a line edge, so
* both cases take the span.
*/
if (isComma) {
if (isSpacingLiteral) {
return <span className="mx-1" />
}

Expand Down
Loading