Skip to content

Commit adfcf60

Browse files
author
Carla Goncalves
committed
Add date picker
1 parent 271a0a6 commit adfcf60

4 files changed

Lines changed: 278 additions & 32 deletions

File tree

apps/eclipse/content/design-system/molecules/datepicker.mdx

Lines changed: 164 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ import {
77
DatePickerSingleExample,
88
DatePickerRangeExample,
99
DatePickerRangeWithPresetsExample,
10+
DatePickerErrorExample,
11+
DatePickerDisabledExample,
12+
DatePickerWithValidationExample,
1013
} from "../../../src/components/date-picker-examples";
1114

1215
### Usage
@@ -122,6 +125,9 @@ export function UnifiedExample() {
122125
- `disabled` - Disabled dates (can be a function, date, or array) (optional)
123126
- `className` - Custom className for the trigger button (optional)
124127
- `align` - Align popover content: `"start"`, `"center"`, or `"end"` (default: `"start"`)
128+
- `isErrored` - Whether the date picker is in an error state (optional, default: `false`)
129+
- `disabledBtn` - Whether the trigger button is disabled (optional, default: `false`)
130+
- `dateFormat` - Date format string for displaying dates using date-fns format tokens (optional)
125131

126132
#### Single Date Mode Props
127133

@@ -316,25 +322,161 @@ const disabledDates = [
316322
/>
317323
```
318324

325+
### Error State
326+
327+
Use the `isErrored` prop to indicate validation errors:
328+
329+
```tsx
330+
import { DatePickerSingle } from "@prisma-docs/eclipse";
331+
import { useState } from "react";
332+
333+
export function FormWithValidation() {
334+
const [date, setDate] = useState<Date>();
335+
const [submitted, setSubmitted] = useState(false);
336+
337+
const hasError = submitted && !date;
338+
339+
return (
340+
<div className="space-y-2">
341+
<DatePickerSingle
342+
date={date}
343+
onDateChange={setDate}
344+
placeholder="Select a date"
345+
isErrored={hasError}
346+
/>
347+
{hasError && (
348+
<p className="text-sm text-foreground-error">
349+
Date is required
350+
</p>
351+
)}
352+
</div>
353+
);
354+
}
355+
```
356+
357+
**Live Example (Static Error State):**
358+
359+
<div className="my-4 max-w-md">
360+
<DatePickerErrorExample />
361+
</div>
362+
363+
**Live Example (With Validation):**
364+
365+
<div className="my-4 max-w-md">
366+
<DatePickerWithValidationExample />
367+
</div>
368+
369+
The error state adds a red ring (`ring-2 ring-stroke-error`) to the button and changes the text and icon color to `text-foreground-error` to indicate the validation error.
370+
371+
### Disabled State
372+
373+
Use the `disabledBtn` prop to disable the entire date picker button:
374+
375+
```tsx
376+
<DatePickerSingle
377+
date={date}
378+
onDateChange={setDate}
379+
placeholder="Disabled picker"
380+
disabledBtn={true}
381+
/>
382+
```
383+
384+
**Live Example:**
385+
386+
<div className="my-4 max-w-md">
387+
<DatePickerDisabledExample />
388+
</div>
389+
390+
**Use Cases:**
391+
- Disable based on permissions
392+
- Disable while data is loading
393+
- Disable when form is submitting
394+
- Conditional disabling based on other form fields
395+
396+
```tsx
397+
function ConditionalDatePicker() {
398+
const [isSubscribed, setIsSubscribed] = useState(false);
399+
const [date, setDate] = useState<Date>();
400+
401+
return (
402+
<div className="space-y-4">
403+
<label>
404+
<input
405+
type="checkbox"
406+
checked={isSubscribed}
407+
onChange={(e) => setIsSubscribed(e.target.checked)}
408+
/>
409+
Enable premium features
410+
</label>
411+
412+
<DatePickerSingle
413+
date={date}
414+
onDateChange={setDate}
415+
placeholder="Premium feature - select date"
416+
disabledBtn={!isSubscribed}
417+
/>
418+
</div>
419+
);
420+
}
421+
```
422+
319423
### Formatting
320424

321-
The component uses `date-fns` for date formatting:
425+
The component uses `date-fns` for date formatting. You can customize the format using the `dateFormat` prop:
322426

427+
**Default Formats:**
323428
- **Single date**: `PPP` format (e.g., "April 29, 2024")
324429
- **Date range**: `LLL dd, y` format (e.g., "Apr 01, 2024 - Apr 30, 2024")
325430

326-
To customize formatting, you can format the dates in your component:
431+
**Custom Formats:**
327432

328433
```tsx
329-
import { format } from "date-fns";
434+
// European format: dd/MM/yyyy
435+
<DatePickerSingle
436+
date={date}
437+
onDateChange={setDate}
438+
dateFormat="dd/MM/yyyy"
439+
placeholder="Select date"
440+
/>
441+
// Output: "17/02/2026"
442+
443+
// US format: MM/dd/yyyy
444+
<DatePickerSingle
445+
date={date}
446+
onDateChange={setDate}
447+
dateFormat="MM/dd/yyyy"
448+
/>
449+
// Output: "02/17/2026"
330450

451+
// ISO format: yyyy-MM-dd
331452
<DatePickerSingle
332453
date={date}
333454
onDateChange={setDate}
334-
placeholder={date ? format(date, "MM/dd/yyyy") : "Select date"}
455+
dateFormat="yyyy-MM-dd"
456+
/>
457+
// Output: "2026-02-17"
458+
459+
// Custom verbose format
460+
<DatePickerRange
461+
dateRange={dateRange}
462+
onDateRangeChange={setDateRange}
463+
dateFormat="EEEE, MMMM do, yyyy"
335464
/>
465+
// Output: "Tuesday, February 17th, 2026 - Friday, February 20th, 2026"
336466
```
337467

468+
**Common date-fns Format Tokens:**
469+
- `dd` - Day of month (01-31)
470+
- `MM` - Month (01-12)
471+
- `yyyy` - Full year (2026)
472+
- `yy` - 2-digit year (26)
473+
- `MMM` - Short month name (Feb)
474+
- `MMMM` - Full month name (February)
475+
- `do` - Day with ordinal (17th)
476+
- `EEEE` - Full day name (Tuesday)
477+
- `PPP` - Long localized date (February 17th, 2026)
478+
- `P` - Short localized date (02/17/2026)
479+
338480
### Best Practices
339481

340482
- Use **single date picker** for events, deadlines, or appointments
@@ -346,6 +488,9 @@ import { format } from "date-fns";
346488
- Add **validation** to ensure date ranges make sense
347489
- Show **clear labels** above date pickers in forms
348490
- Use **consistent date formats** across your application
491+
- Use `isErrored` prop with validation messages for better UX
492+
- Use `disabledBtn` for conditional access or loading states
493+
- Choose appropriate `dateFormat` based on your user's locale and preferences
349494

350495
### Accessibility
351496

@@ -397,19 +542,28 @@ import { useForm, Controller } from "react-hook-form";
397542
import { DatePickerSingle } from "@prisma-docs/eclipse";
398543

399544
function MyForm() {
400-
const { control, handleSubmit } = useForm();
545+
const { control, handleSubmit, formState: { errors } } = useForm();
401546

402547
return (
403548
<form onSubmit={handleSubmit(onSubmit)}>
404549
<Controller
405550
control={control}
406551
name="eventDate"
552+
rules={{ required: "Event date is required" }}
407553
render={({ field }) => (
408-
<DatePickerSingle
409-
date={field.value}
410-
onDateChange={field.onChange}
411-
placeholder="Select event date"
412-
/>
554+
<div className="space-y-2">
555+
<DatePickerSingle
556+
date={field.value}
557+
onDateChange={field.onChange}
558+
placeholder="Select event date"
559+
isErrored={!!errors.eventDate}
560+
/>
561+
{errors.eventDate && (
562+
<p className="text-sm text-foreground-error">
563+
{errors.eventDate.message}
564+
</p>
565+
)}
566+
</div>
413567
)}
414568
/>
415569
</form>

apps/eclipse/src/components/date-picker-examples.tsx

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ export function DatePickerSingleExample() {
1515
<DatePickerSingle
1616
date={date}
1717
onDateChange={setDate}
18-
placeholder="Pick a date"
18+
placeholder="Select a date"
19+
dateFormat="dd/MM/yyyy"
1920
/>
2021
);
2122
}
@@ -45,3 +46,61 @@ export function DatePickerRangeWithPresetsExample() {
4546
/>
4647
);
4748
}
49+
50+
export function DatePickerErrorExample() {
51+
return (
52+
<div className="space-y-2">
53+
<DatePickerSingle placeholder="Error state example" isErrored={true} />
54+
<p className="text-sm text-foreground-error">Date is required</p>
55+
</div>
56+
);
57+
}
58+
59+
export function DatePickerDisabledExample() {
60+
return (
61+
<DatePickerSingle placeholder="Disabled state example" disabledBtn={true} />
62+
);
63+
}
64+
65+
export function DatePickerWithValidationExample() {
66+
const [date, setDate] = useState<Date>();
67+
const [submitted, setSubmitted] = useState(false);
68+
69+
const hasError = submitted && !date;
70+
71+
const handleSubmit = (e: React.FormEvent) => {
72+
e.preventDefault();
73+
setSubmitted(true);
74+
if (date) {
75+
alert(`Date selected: ${date.toLocaleDateString()}`);
76+
}
77+
};
78+
79+
return (
80+
<form onSubmit={handleSubmit} className="space-y-4">
81+
<div className="space-y-2">
82+
<label className="text-sm font-medium">Event Date *</label>
83+
<DatePickerSingle
84+
date={date}
85+
onDateChange={(newDate) => {
86+
setDate(newDate);
87+
if (newDate) setSubmitted(false);
88+
}}
89+
placeholder="Select event date"
90+
isErrored={hasError}
91+
/>
92+
{hasError && (
93+
<p className="text-sm text-foreground-error">
94+
Please select an event date
95+
</p>
96+
)}
97+
</div>
98+
<button
99+
type="submit"
100+
className="px-4 py-2 bg-background-ppg text-foreground-ppg rounded-md hover:bg-background-ppg/90"
101+
>
102+
Submit
103+
</button>
104+
</form>
105+
);
106+
}

0 commit comments

Comments
 (0)