Skip to content

Commit 5b75166

Browse files
authored
test: date picker (#1844)
1 parent 1c75cec commit 5b75166

File tree

3 files changed

+908
-0
lines changed

3 files changed

+908
-0
lines changed

tests/src/tests/date-field/date-field.browser.test.ts

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -996,6 +996,113 @@ describe("date field", () => {
996996
await expect.element(t.month).toHaveTextContent("mm");
997997
await expect.element(t.year).toHaveTextContent("yyyy");
998998
});
999+
1000+
it("should allow typing hours 0-23 with non en-US locales that use 24-hour format", async () => {
1001+
if (navigator.userAgent.includes("WebKit")) {
1002+
expect(true);
1003+
return;
1004+
}
1005+
1006+
setup({
1007+
granularity: "minute",
1008+
locale: "de-DE", // german uses 24-hour format
1009+
});
1010+
1011+
const { getHour } = getTimeSegments(page.getByTestId);
1012+
const hour = getHour();
1013+
1014+
// should not have dayPeriod for 24-hour locales
1015+
await expectNotExists(page.getByTestId("dayPeriod"));
1016+
1017+
// test typing single digit hours > 2 (issue: these get clamped to 12-hour format)
1018+
await hour.click();
1019+
await userEvent.keyboard("3");
1020+
await expect.element(hour).toHaveTextContent("03");
1021+
1022+
// test typing hours 13-23 (issue: these should work but currently clamp)
1023+
await hour.click();
1024+
await userEvent.keyboard("15");
1025+
await expect.element(hour).toHaveTextContent("15");
1026+
1027+
await hour.click();
1028+
await userEvent.keyboard("23");
1029+
await expect.element(hour).toHaveTextContent("23");
1030+
1031+
await hour.click();
1032+
await userEvent.keyboard("18");
1033+
await expect.element(hour).toHaveTextContent("18");
1034+
});
1035+
1036+
it("should allow arrow key navigation through full 0-23 range with 24-hour locales", async () => {
1037+
if (navigator.userAgent.includes("WebKit")) {
1038+
expect(true);
1039+
return;
1040+
}
1041+
1042+
const value = new CalendarDateTime(2023, 10, 12, 14, 30, 30, 0);
1043+
setup({
1044+
value,
1045+
granularity: "minute",
1046+
locale: "fr-FR", // french uses 24-hour format
1047+
});
1048+
1049+
const { getHour } = getTimeSegments(page.getByTestId);
1050+
const hour = getHour();
1051+
1052+
await expect.element(hour).toHaveTextContent("14");
1053+
1054+
// arrow up should go to 15, not clamp to 12
1055+
await hour.click();
1056+
await userEvent.keyboard(kbd.ARROW_UP);
1057+
await expect.element(hour).toHaveTextContent("15");
1058+
1059+
// continue to 23
1060+
for (let i = 0; i < 8; i++) {
1061+
await userEvent.keyboard(kbd.ARROW_UP);
1062+
}
1063+
await expect.element(hour).toHaveTextContent("23");
1064+
1065+
// arrow up from 23 should cycle to 00
1066+
await userEvent.keyboard(kbd.ARROW_UP);
1067+
await expect.element(hour).toHaveTextContent("00");
1068+
1069+
// arrow down from 00 should go to 23
1070+
await userEvent.keyboard(kbd.ARROW_DOWN);
1071+
await expect.element(hour).toHaveTextContent("23");
1072+
});
1073+
1074+
it("should display and allow typing hours > 12 with sv-SE locale (24-hour format)", async () => {
1075+
if (navigator.userAgent.includes("WebKit")) {
1076+
expect(true);
1077+
return;
1078+
}
1079+
1080+
const value = new CalendarDateTime(2023, 10, 12, 18, 30, 30, 0);
1081+
setup({
1082+
value,
1083+
granularity: "minute",
1084+
locale: "sv-SE", // swedish uses 24-hour format
1085+
});
1086+
1087+
const { getHour } = getTimeSegments(page.getByTestId);
1088+
const hour = getHour();
1089+
1090+
// should display 18, not clamp to 12 or convert to 12-hour format
1091+
await expect.element(hour).toHaveTextContent("18");
1092+
1093+
// should not have dayPeriod segment
1094+
await expectNotExists(page.getByTestId("dayPeriod"));
1095+
1096+
// typing should allow values > 12
1097+
await hour.click();
1098+
await userEvent.keyboard("20");
1099+
await expect.element(hour).toHaveTextContent("20");
1100+
1101+
// arrow down should work correctly (not clamp to 1-12 range)
1102+
await hour.click();
1103+
await userEvent.keyboard(kbd.ARROW_DOWN);
1104+
await expect.element(hour).toHaveTextContent("19");
1105+
});
9991106
});
10001107

10011108
/**
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
<script lang="ts" module>
2+
import { DatePicker, type WithoutChildrenOrChild } from "bits-ui";
3+
export type DatePickerTestProps = WithoutChildrenOrChild<DatePicker.RootProps>;
4+
</script>
5+
6+
<script lang="ts">
7+
let { placeholder, value, open = false, ...restProps }: DatePickerTestProps = $props();
8+
9+
function clear() {
10+
value = undefined;
11+
}
12+
</script>
13+
14+
<main>
15+
<div data-testid="value">{String(value)}</div>
16+
<div data-testid="open">{open}</div>
17+
<button onclick={clear} data-testid="clear">clear</button>
18+
<button onclick={() => (open = !open)} data-testid="toggle-open">toggle open</button>
19+
<DatePicker.Root bind:value bind:placeholder bind:open {...restProps}>
20+
<DatePicker.Label data-testid="label">Date</DatePicker.Label>
21+
<DatePicker.Input data-testid="input">
22+
{#snippet children({ segments })}
23+
{#each segments as { part, value }, i (i)}
24+
<DatePicker.Segment {part} data-testid={part === "literal" ? undefined : part}>
25+
{value}
26+
</DatePicker.Segment>
27+
{/each}
28+
{/snippet}
29+
</DatePicker.Input>
30+
31+
<DatePicker.Trigger data-testid="trigger">Open</DatePicker.Trigger>
32+
<DatePicker.Content data-testid="content">
33+
<DatePicker.Calendar data-testid="calendar">
34+
{#snippet children({ months, weekdays })}
35+
<DatePicker.Header data-testid="header">
36+
<DatePicker.PrevButton data-testid="prev-button">Prev</DatePicker.PrevButton
37+
>
38+
<DatePicker.Heading data-testid="heading" />
39+
<DatePicker.NextButton data-testid="next-button">Next</DatePicker.NextButton
40+
>
41+
</DatePicker.Header>
42+
<div>
43+
{#each months as month, i (i)}
44+
{@const m = month.value.month}
45+
<DatePicker.Grid data-testid="grid-{m}">
46+
<DatePicker.GridHead data-testid="grid-head-{m}">
47+
<DatePicker.GridRow data-testid="grid-row-{m}">
48+
{#each weekdays as day, i (i)}
49+
<DatePicker.HeadCell data-testid="weekday-{m}-{i}">
50+
{day}
51+
</DatePicker.HeadCell>
52+
{/each}
53+
</DatePicker.GridRow>
54+
</DatePicker.GridHead>
55+
<DatePicker.GridBody data-testid="grid-body-{m}">
56+
{#each month.weeks as weekDates, i (i)}
57+
<DatePicker.GridRow
58+
data-testid="grid-row-{m}-{i}"
59+
data-week
60+
>
61+
{#each weekDates as date, d (d)}
62+
<DatePicker.Cell
63+
{date}
64+
month={month.value}
65+
data-testid="cell-{date.month}-{d}"
66+
class="p-3"
67+
>
68+
<DatePicker.Day
69+
data-testid="date-{date.month}-{date.day}"
70+
class="p-1"
71+
>
72+
{date.day}
73+
</DatePicker.Day>
74+
</DatePicker.Cell>
75+
{/each}
76+
</DatePicker.GridRow>
77+
{/each}
78+
</DatePicker.GridBody>
79+
</DatePicker.Grid>
80+
{/each}
81+
</div>
82+
{/snippet}
83+
</DatePicker.Calendar>
84+
</DatePicker.Content>
85+
</DatePicker.Root>
86+
</main>

0 commit comments

Comments
 (0)