diff --git a/packages/lab/src/date-picker/DatePickerActions.tsx b/packages/lab/src/date-picker/DatePickerActions.tsx
index a5fc8e2cfd5..c0e98927df4 100644
--- a/packages/lab/src/date-picker/DatePickerActions.tsx
+++ b/packages/lab/src/date-picker/DatePickerActions.tsx
@@ -101,6 +101,7 @@ export const DatePickerActions = forwardRef<
onApply,
onCancel,
selectionVariant,
+ ...rest
} = props;
const targetWindow = useWindow();
useComponentCssInjection({
@@ -148,10 +149,11 @@ export const DatePickerActions = forwardRef<
};
return (
-
+
{children}
);
diff --git a/site/src/examples/calendar/UnselectableDates.tsx b/site/src/examples/calendar/UnselectableDates.tsx
index a36f94480c5..8d3531a74da 100644
--- a/site/src/examples/calendar/UnselectableDates.tsx
+++ b/site/src/examples/calendar/UnselectableDates.tsx
@@ -1,5 +1,11 @@
import { type DateValue, getDayOfWeek } from "@internationalized/date";
-import { Calendar, CalendarNavigation, getCurrentLocale } from "@salt-ds/lab";
+import {
+ Calendar,
+ CalendarDateGrid,
+ CalendarNavigation,
+ CalendarWeekHeader,
+ getCurrentLocale,
+} from "@salt-ds/lab";
import type { ReactElement } from "react";
// Saturday & Sunday
@@ -11,5 +17,7 @@ const isDayUnselectable = (date: DateValue) =>
export const UnselectableDates = (): ReactElement => (
+
+
);
diff --git a/site/src/examples/calendar/WithLocale.tsx b/site/src/examples/calendar/WithLocale.tsx
index d66b5a67836..18d82a9983d 100644
--- a/site/src/examples/calendar/WithLocale.tsx
+++ b/site/src/examples/calendar/WithLocale.tsx
@@ -1,9 +1,15 @@
-import { getLocalTimeZone, today } from "@internationalized/date";
-import { Calendar, CalendarNavigation } from "@salt-ds/lab";
+import {
+ Calendar,
+ CalendarDateGrid,
+ CalendarNavigation,
+ CalendarWeekHeader,
+} from "@salt-ds/lab";
import type { ReactElement } from "react";
export const WithLocale = (): ReactElement => (
+
+
);
diff --git a/site/src/examples/calendar/index.ts b/site/src/examples/calendar/index.ts
index 436eff30888..99639c06d6b 100644
--- a/site/src/examples/calendar/index.ts
+++ b/site/src/examples/calendar/index.ts
@@ -8,8 +8,8 @@ export * from "./DisabledDates";
export * from "./HighlightedDates";
export * from "./HideOutOfRangeDates";
export * from "./HideYearDropdown";
-export * from "./CustomHeader";
export * from "./CustomDayRender";
export * from "./MinMaxDate";
+export * from "./TodayButton";
export * from "./TwinCalendars";
export * from "./WithLocale";
diff --git a/site/src/examples/date-picker/Range.tsx b/site/src/examples/date-picker/Range.tsx
index 2e566501f5a..a2d29fa40a1 100644
--- a/site/src/examples/date-picker/Range.tsx
+++ b/site/src/examples/date-picker/Range.tsx
@@ -7,7 +7,7 @@ import {
formatDate,
getCurrentLocale,
} from "@salt-ds/lab";
-import type { ReactElement } from "react";
+import { type ReactElement, useCallback } from "react";
function formatDateRange(
dateRange: DateRangeSelection | null,
@@ -23,16 +23,23 @@ function formatDateRange(
: endDate;
return `Start date: ${formattedStartDate}, End date: ${formattedEndDate}`;
}
-export const Range = (): ReactElement => (
-
{
+export const Range = (): ReactElement => {
+ const handleSelectedDateChange = useCallback(
+ (newSelectedDate: DateRangeSelection | null) => {
console.log(`Selected date range: ${formatDateRange(newSelectedDate)}`);
- }}
- >
-
-
-
-
-
-);
+ },
+ [],
+ );
+
+ return (
+
+
+
+
+
+
+ );
+};
diff --git a/site/src/examples/date-picker/RangeBordered.tsx b/site/src/examples/date-picker/RangeBordered.tsx
index d3dbc249f5f..dceab327b63 100644
--- a/site/src/examples/date-picker/RangeBordered.tsx
+++ b/site/src/examples/date-picker/RangeBordered.tsx
@@ -7,7 +7,7 @@ import {
formatDate,
getCurrentLocale,
} from "@salt-ds/lab";
-import React, { type ReactElement } from "react";
+import React, { type ReactElement, useCallback } from "react";
function formatDateRange(
dateRange: DateRangeSelection | null,
@@ -24,25 +24,32 @@ function formatDateRange(
return `Start date: ${formattedStartDate}, End date: ${formattedEndDate}`;
}
-export const RangeBordered = (): ReactElement => (
-
{
+export const RangeBordered = (): ReactElement => {
+ const handleSelectedDateChange = useCallback(
+ (newSelectedDate: DateRangeSelection | null) => {
console.log(`Selected date range: ${formatDateRange(newSelectedDate)}`);
- }}
- >
-
-
-
-
-
-);
+ },
+ [],
+ );
+
+ return (
+
+
+
+
+
+
+ );
+};
diff --git a/site/src/examples/date-picker/RangeControlled.tsx b/site/src/examples/date-picker/RangeControlled.tsx
index d0e49877a51..685fcc54e02 100644
--- a/site/src/examples/date-picker/RangeControlled.tsx
+++ b/site/src/examples/date-picker/RangeControlled.tsx
@@ -7,7 +7,7 @@ import {
formatDate,
getCurrentLocale,
} from "@salt-ds/lab";
-import { type ReactElement, useState } from "react";
+import { type ReactElement, useCallback, useState } from "react";
function formatDateRange(
dateRange: DateRangeSelection | null,
@@ -28,14 +28,22 @@ export const RangeControlled = (): ReactElement => {
const [selectedDate, setSelectedDate] = useState
(
null,
);
+ const handleSelectedDateChange = useCallback(
+ (
+ newSelectedDate: DateRangeSelection | null,
+ error: { startDate: string | false; endDate: string | false },
+ ) => {
+ console.log(`Selected date range: ${formatDateRange(newSelectedDate)}`);
+ setSelectedDate(newSelectedDate);
+ },
+ [setSelectedDate],
+ );
+
return (
{
- console.log(`Selected date range: ${formatDateRange(newSelectedDate)}`);
- setSelectedDate(newSelectedDate);
- }}
+ onSelectedDateChange={handleSelectedDateChange}
>
diff --git a/site/src/examples/date-picker/RangeWithConfirmation.tsx b/site/src/examples/date-picker/RangeWithConfirmation.tsx
index a1ebaa3a8f1..74d005b55a0 100644
--- a/site/src/examples/date-picker/RangeWithConfirmation.tsx
+++ b/site/src/examples/date-picker/RangeWithConfirmation.tsx
@@ -17,7 +17,7 @@ import {
formatDate,
getCurrentLocale,
} from "@salt-ds/lab";
-import React, { type ReactElement, useRef, useState } from "react";
+import React, { type ReactElement, useCallback, useRef, useState } from "react";
function formatDateRange(
dateRange: DateRangeSelection | null,
@@ -46,7 +46,10 @@ function isValidDateRange(date: DateRangeSelection | null) {
}
export const RangeWithConfirmation = (): ReactElement => {
- const helperText = "Select range (DD MMM YYYY - DD MMM YYYY)";
+ const defaultHelperText =
+ "Select range (DD MMM YYYY - DD MMM YYYY) e.g. 09 Jun 2024";
+ const errorHelperText = "Please enter a valid date in DD MMM YYYY format";
+ const [helperText, setHelperText] = useState(defaultHelperText);
const applyButtonRef = useRef(null);
const minDate = today(getLocalTimeZone());
const [validationStatus, setValidationStatus] = useState<"error" | undefined>(
@@ -55,6 +58,37 @@ export const RangeWithConfirmation = (): ReactElement => {
const [selectedDate, setSelectedDate] = useState(
null,
);
+ const handleApply = useCallback(
+ (
+ newSelectedDate: DateRangeSelection | null,
+ error: {
+ startDate: string | false;
+ endDate: string | false;
+ },
+ ) => {
+ console.log(`Selected date range: ${formatDateRange(newSelectedDate)}`);
+ const validationStatus =
+ !error.startDate && !error.endDate && isValidDateRange(newSelectedDate)
+ ? undefined
+ : "error";
+ if (validationStatus === "error") {
+ setHelperText(errorHelperText);
+ } else {
+ setHelperText(defaultHelperText);
+ }
+ setValidationStatus(validationStatus);
+ },
+ [setValidationStatus, setHelperText],
+ );
+ const handleSelectedDateChange = useCallback(
+ (newSelectedDate: DateRangeSelection | null) => {
+ setSelectedDate(newSelectedDate);
+ if (newSelectedDate?.startDate && newSelectedDate?.endDate) {
+ applyButtonRef?.current?.focus();
+ }
+ },
+ [applyButtonRef?.current, setSelectedDate],
+ );
return (
@@ -63,25 +97,9 @@ export const RangeWithConfirmation = (): ReactElement => {
selectionVariant="range"
minDate={minDate}
maxDate={minDate.add({ years: 50 })}
+ onApply={handleApply}
+ onSelectedDateChange={handleSelectedDateChange}
selectedDate={selectedDate}
- onApply={(newSelectedDate, error) => {
- console.log(
- `Selected date range: ${formatDateRange(newSelectedDate)}`,
- );
- const validationStatus =
- !error.startDate &&
- !error.endDate &&
- isValidDateRange(newSelectedDate)
- ? undefined
- : "error";
- setValidationStatus(validationStatus);
- }}
- onSelectedDateChange={(newSelectedDate, error) => {
- setSelectedDate(newSelectedDate);
- if (newSelectedDate?.startDate && newSelectedDate?.endDate) {
- applyButtonRef?.current?.focus();
- }
- }}
>
diff --git a/site/src/examples/date-picker/RangeWithCustomPanel.tsx b/site/src/examples/date-picker/RangeWithCustomPanel.tsx
index 2c9e0c361f6..28ed2b9f941 100644
--- a/site/src/examples/date-picker/RangeWithCustomPanel.tsx
+++ b/site/src/examples/date-picker/RangeWithCustomPanel.tsx
@@ -13,7 +13,7 @@ import {
getCurrentLocale,
} from "@salt-ds/lab";
import { CustomDatePickerPanel } from "@salt-ds/lab/stories/date-picker/CustomDatePickerPanel";
-import React, { type ReactElement } from "react";
+import React, { type ReactElement, useCallback } from "react";
function formatDateRange(
dateRange: DateRangeSelection | null,
@@ -33,16 +33,21 @@ function formatDateRange(
export const RangeWithCustomPanel = (): ReactElement => {
const helperText = "Date format DD MMM YYYY (e.g. 09 Jun 2024)";
const minDate = today(getLocalTimeZone());
+ const handleSelectedDateChange = useCallback(
+ (newSelectedDate: DateRangeSelection | null) => {
+ console.log(`Selected date range: ${formatDateRange(newSelectedDate)}`);
+ },
+ [],
+ );
+
return (
Select a date range
{
- console.log(`Selected date: ${formatDateRange(newSelectedDate)}`);
- }}
+ onSelectedDateChange={handleSelectedDateChange}
>
diff --git a/site/src/examples/date-picker/RangeWithFormField.tsx b/site/src/examples/date-picker/RangeWithFormField.tsx
index 75f76ef7d2f..9193f0416bc 100644
--- a/site/src/examples/date-picker/RangeWithFormField.tsx
+++ b/site/src/examples/date-picker/RangeWithFormField.tsx
@@ -12,7 +12,7 @@ import {
formatDate,
getCurrentLocale,
} from "@salt-ds/lab";
-import { type ReactElement, useState } from "react";
+import { type ReactElement, useCallback, useState } from "react";
function formatDateRange(
dateRange: DateRangeSelection | null,
@@ -41,12 +41,31 @@ function isValidDateRange(date: DateRangeSelection | null) {
}
export const RangeWithFormField = (): ReactElement => {
- const helperText = "Select range (DD MMM YYYY - DD MMM YYYY)";
+ const defaultHelperText =
+ "Select range DD MMM YYYY - DD MMM YYYY (e.g. 09 Jun 2024)";
+ const errorHelperText = "Please enter a valid date in DD MMM YYYY format";
+ const [helperText, setHelperText] = useState(defaultHelperText);
const [validationStatus, setValidationStatus] = useState<"error" | undefined>(
undefined,
);
- const [selectedDate, setSelectedDate] = useState(
- null,
+ const handleSelectedDateChange = useCallback(
+ (
+ newSelectedDate: DateRangeSelection | null,
+ error: { startDate: string | false; endDate: string | false },
+ ) => {
+ console.log(`Selected date range: ${formatDateRange(newSelectedDate)}`);
+ const validationStatus =
+ !error.startDate && !error.endDate && isValidDateRange(newSelectedDate)
+ ? undefined
+ : "error";
+ setValidationStatus(validationStatus);
+ if (validationStatus === "error") {
+ setHelperText(errorHelperText);
+ } else {
+ setHelperText(defaultHelperText);
+ }
+ },
+ [setValidationStatus, setHelperText],
);
return (
@@ -54,20 +73,7 @@ export const RangeWithFormField = (): ReactElement => {
Select a date range
{
- console.log(
- `Selected date range: ${formatDateRange(newSelectedDate)}`,
- );
- setSelectedDate(newSelectedDate);
- const validationStatus =
- !error.startDate &&
- !error.endDate &&
- isValidDateRange(newSelectedDate)
- ? undefined
- : "error";
- setValidationStatus(validationStatus);
- }}
+ onSelectedDateChange={handleSelectedDateChange}
>
diff --git a/site/src/examples/date-picker/RangeWithInitialError.tsx b/site/src/examples/date-picker/RangeWithInitialError.tsx
index e11120dd185..1af51ecaded 100644
--- a/site/src/examples/date-picker/RangeWithInitialError.tsx
+++ b/site/src/examples/date-picker/RangeWithInitialError.tsx
@@ -13,7 +13,7 @@ import {
formatDate,
getCurrentLocale,
} from "@salt-ds/lab";
-import { type ReactElement, useState } from "react";
+import { type ReactElement, useCallback, useState } from "react";
function formatDateRange(
dateRange: DateRangeSelection | null,
@@ -42,29 +42,40 @@ function isValidDateRange(date: DateRangeSelection | null) {
}
export const RangeWithInitialError = (): ReactElement => {
- const helperText = "Select range (DD MMM YYYY - DD MMM YYYY)";
+ const defaultHelperText =
+ "Select range DD MMM YYYY - DD MMM YYYY (e.g. 09 Jun 2024)";
+ const errorHelperText = "Please enter a valid date in DD MMM YYYY format";
+ const [helperText, setHelperText] = useState(errorHelperText);
const [validationStatus, setValidationStatus] = useState<"error" | undefined>(
"error",
);
+ const handleSelectedDateChange = useCallback(
+ (
+ newSelectedDate: DateRangeSelection | null,
+ error: { startDate: string | false; endDate: string | false },
+ ) => {
+ console.log(`Selected date range: ${formatDateRange(newSelectedDate)}`);
+ const validationStatus =
+ !error.startDate && !error.endDate && isValidDateRange(newSelectedDate)
+ ? undefined
+ : "error";
+ if (validationStatus === "error") {
+ setHelperText(errorHelperText);
+ } else {
+ setHelperText(defaultHelperText);
+ }
+ setValidationStatus(validationStatus);
+ },
+ [setValidationStatus, setHelperText],
+ );
return (
Select a date range
{
- console.log(
- `Selected date range: ${formatDateRange(newSelectedDate)}`,
- );
- const validationStatus =
- !error.startDate &&
- !error.endDate &&
- isValidDateRange(newSelectedDate)
- ? undefined
- : "error";
- setValidationStatus(validationStatus);
- }}
defaultSelectedDate={{ startDate: new CalendarDate(2024, 6, 9) }}
+ onSelectedDateChange={handleSelectedDateChange}
>
{
- if (!dateString) {
- return { date: null, error: false };
- }
- const dateParts = dateString.match(/^(\d{1,2})\/(\d{1,2})\/(\d{4})$/);
- if (!dateParts) {
- return { date: null, error: "invalid date" };
- }
- const [, day, month, year] = dateParts;
- return {
- date: new CalendarDate(
- Number.parseInt(year, 10),
- Number.parseInt(month, 10),
- Number.parseInt(day, 10),
- ),
- error: false,
- };
-};
-
-const formatDateEsES = (date: DateValue | null) => {
- return date
- ? new DateFormatter("es-ES", {
- day: "2-digit",
- month: "2-digit",
- year: "numeric",
- }).format(date.toDate(getLocalTimeZone()))
- : "";
-};
-
export const RangeWithLocaleEsES = (): ReactElement => {
const locale = "es-ES";
- const [selectedDate, setSelectedDate] = useState(
- null,
- );
- const helperText = `Locale ${locale}`;
+ const defaultHelperText = `Locale ${locale}`;
+ const errorHelperText = "Please enter a valid date in DD MMM YYYY format";
+ const [helperText, setHelperText] = useState(defaultHelperText);
const [validationStatus, setValidationStatus] = useState<"error" | undefined>(
undefined,
);
+ const handleSelectedDateChange = useCallback(
+ (
+ newSelectedDate: DateRangeSelection | null,
+ error: {
+ startDate: string | false;
+ endDate: string | false;
+ },
+ ) => {
+ console.log(
+ `Selected date range: ${formatDateRange(newSelectedDate, locale, {
+ day: "2-digit",
+ month: "2-digit",
+ year: "numeric",
+ })}`,
+ );
+ const validationStatus =
+ !error.startDate && !error.endDate && isValidDateRange(newSelectedDate)
+ ? undefined
+ : "error";
+ if (validationStatus === "error") {
+ setHelperText(errorHelperText);
+ } else {
+ setHelperText(defaultHelperText);
+ }
+ setValidationStatus(validationStatus);
+ },
+ [setValidationStatus, setHelperText],
+ );
return (
Select a date
{
- console.log(
- `Selected date range: ${formatDateRange(newSelectedDate, locale, {
- day: "2-digit",
- month: "2-digit",
- year: "numeric",
- })}`,
- );
- setSelectedDate(newSelectedDate);
- const validationStatus =
- !error.startDate &&
- !error.endDate &&
- isValidDateRange(newSelectedDate)
- ? undefined
- : "error";
- setValidationStatus(validationStatus);
- }}
+ onSelectedDateChange={handleSelectedDateChange}
>
-
+
-
+
{helperText}
diff --git a/site/src/examples/date-picker/RangeWithMinMaxDate.tsx b/site/src/examples/date-picker/RangeWithMinMaxDate.tsx
index 7c9ffec39a3..c63dd19718c 100644
--- a/site/src/examples/date-picker/RangeWithMinMaxDate.tsx
+++ b/site/src/examples/date-picker/RangeWithMinMaxDate.tsx
@@ -13,7 +13,7 @@ import {
formatDate,
getCurrentLocale,
} from "@salt-ds/lab";
-import { type ReactElement, useState } from "react";
+import { type ReactElement, useCallback, useState } from "react";
function formatDateRange(
dateRange: DateRangeSelection | null,
@@ -31,24 +31,22 @@ function formatDateRange(
}
export const RangeWithMinMaxDate = (): ReactElement => {
- const [selectedDate, setSelectedDate] = useState(
- null,
+ const helperText = "Select date between 15/01/2030 and 15/01/2031";
+ const handleSelectedDateChange = useCallback(
+ (newSelectedDate: DateRangeSelection | null) => {
+ console.log(`Selected date range: ${formatDateRange(newSelectedDate)}`);
+ },
+ [],
);
- const helperText = "Valid between 15/01/2030 and 15/01/2031";
+
return (
Select a date range
{
- console.log(
- `Selected date range: ${formatDateRange(newSelectedDate)}`,
- );
- setSelectedDate(newSelectedDate);
- }}
minDate={new CalendarDate(2030, 1, 15)}
maxDate={new CalendarDate(2031, 1, 15)}
+ onSelectedDateChange={handleSelectedDateChange}
>
diff --git a/site/src/examples/date-picker/Single.tsx b/site/src/examples/date-picker/Single.tsx
index a54baf5ae20..7e69b8123db 100644
--- a/site/src/examples/date-picker/Single.tsx
+++ b/site/src/examples/date-picker/Single.tsx
@@ -4,10 +4,11 @@ import {
DatePickerOverlay,
DatePickerSingleInput,
DatePickerSinglePanel,
+ type SingleDateSelection,
formatDate,
getCurrentLocale,
} from "@salt-ds/lab";
-import type { ReactElement } from "react";
+import { type ReactElement, useCallback } from "react";
function formatSingleDate(
date: DateValue | null,
@@ -20,16 +21,23 @@ function formatSingleDate(
return date;
}
-export const Single = (): ReactElement => (
- {
+export const Single = (): ReactElement => {
+ const handleSelectedDateChange = useCallback(
+ (newSelectedDate: SingleDateSelection | null) => {
console.log(`Selected date: ${formatSingleDate(newSelectedDate)}`);
- }}
- >
-
-
-
-
-
-);
+ },
+ [],
+ );
+
+ return (
+
+
+
+
+
+
+ );
+};
diff --git a/site/src/examples/date-picker/SingleBordered.tsx b/site/src/examples/date-picker/SingleBordered.tsx
index 9ec591c46f2..947673bf0a7 100644
--- a/site/src/examples/date-picker/SingleBordered.tsx
+++ b/site/src/examples/date-picker/SingleBordered.tsx
@@ -4,10 +4,11 @@ import {
DatePickerOverlay,
DatePickerSingleInput,
DatePickerSinglePanel,
+ type SingleDateSelection,
formatDate,
getCurrentLocale,
} from "@salt-ds/lab";
-import React, { type ReactElement } from "react";
+import React, { type ReactElement, useCallback } from "react";
function formatSingleDate(
date: DateValue | null,
@@ -20,21 +21,28 @@ function formatSingleDate(
return date;
}
-export const SingleBordered = (): ReactElement => (
- {
+export const SingleBordered = (): ReactElement => {
+ const handleSelectedDateChange = useCallback(
+ (newSelectedDate: SingleDateSelection | null) => {
console.log(`Selected date: ${formatSingleDate(newSelectedDate)}`);
- }}
- >
-
-
-
-
-
-);
+ },
+ [],
+ );
+
+ return (
+
+
+
+
+
+
+ );
+};
diff --git a/site/src/examples/date-picker/SingleControlled.tsx b/site/src/examples/date-picker/SingleControlled.tsx
index 00e48054ebf..1ca0cc16a17 100644
--- a/site/src/examples/date-picker/SingleControlled.tsx
+++ b/site/src/examples/date-picker/SingleControlled.tsx
@@ -8,7 +8,7 @@ import {
formatDate,
getCurrentLocale,
} from "@salt-ds/lab";
-import { type ReactElement, useState } from "react";
+import { type ReactElement, useCallback, useState } from "react";
function formatSingleDate(
date: DateValue | null,
@@ -25,14 +25,19 @@ export const SingleControlled = (): ReactElement => {
const [selectedDate, setSelectedDate] = useState(
null,
);
+ const handleSelectedDateChange = useCallback(
+ (newSelectedDate: SingleDateSelection | null) => {
+ console.log(`Selected date: ${formatSingleDate(newSelectedDate)}`);
+ setSelectedDate(newSelectedDate);
+ },
+ [setSelectedDate],
+ );
+
return (
{
- console.log(`Selected date: ${formatSingleDate(newSelectedDate)}`);
- setSelectedDate(newSelectedDate);
- }}
+ onSelectedDateChange={handleSelectedDateChange}
>
diff --git a/site/src/examples/date-picker/SingleWithConfirmation.tsx b/site/src/examples/date-picker/SingleWithConfirmation.tsx
index 14aec3aaa89..06e13082318 100644
--- a/site/src/examples/date-picker/SingleWithConfirmation.tsx
+++ b/site/src/examples/date-picker/SingleWithConfirmation.tsx
@@ -17,7 +17,7 @@ import {
formatDate,
getCurrentLocale,
} from "@salt-ds/lab";
-import React, { type ReactElement, useRef, useState } from "react";
+import React, { type ReactElement, useCallback, useRef, useState } from "react";
function formatSingleDate(
date: DateValue | null,
@@ -31,7 +31,9 @@ function formatSingleDate(
}
export const SingleWithConfirmation = (): ReactElement => {
- const helperText = "Select range (DD MMM YYYY - DD MMM YYYY)";
+ const defaultHelperText = "Date format DD MMM YYYY (e.g. 09 Jun 2024)";
+ const errorHelperText = "Please enter a valid date in DD MMM YYYY format";
+ const [helperText, setHelperText] = useState(defaultHelperText);
const applyButtonRef = useRef(null);
const [validationStatus, setValidationStatus] = useState<"error" | undefined>(
undefined,
@@ -39,26 +41,40 @@ export const SingleWithConfirmation = (): ReactElement => {
const [selectedDate, setSelectedDate] = useState(
null,
);
+ const handleApply = useCallback(
+ (newSelectedDate: SingleDateSelection | null, error: string | false) => {
+ console.log(`Selected date: ${formatSingleDate(newSelectedDate)}`);
+ if (error) {
+ setHelperText(errorHelperText);
+ } else {
+ setHelperText(defaultHelperText);
+ }
+ setValidationStatus(error ? "error" : undefined);
+ },
+ [setSelectedDate, setHelperText],
+ );
+ const handleSelectedDateChange = useCallback(
+ (newSelectedDate: SingleDateSelection | null) => {
+ setSelectedDate(newSelectedDate);
+ applyButtonRef?.current?.focus();
+ },
+ [applyButtonRef?.current, setSelectedDate],
+ );
+
return (
Select a date
{
- console.log(`Selected date: ${formatSingleDate(newSelectedDate)}`);
- setValidationStatus(error ? "error" : undefined);
- }}
- onSelectedDateChange={(newSelectedDate, error) => {
- setSelectedDate(newSelectedDate);
- applyButtonRef?.current?.focus();
- }}
>
-
+
diff --git a/site/src/examples/date-picker/SingleWithCustomPanel.tsx b/site/src/examples/date-picker/SingleWithCustomPanel.tsx
index 757e1ac4830..2dd445e8623 100644
--- a/site/src/examples/date-picker/SingleWithCustomPanel.tsx
+++ b/site/src/examples/date-picker/SingleWithCustomPanel.tsx
@@ -12,11 +12,12 @@ import {
DatePicker,
DatePickerOverlay,
DatePickerSingleInput,
+ type SingleDateSelection,
formatDate,
getCurrentLocale,
} from "@salt-ds/lab";
import { CustomDatePickerPanel } from "@salt-ds/lab/stories/date-picker/CustomDatePickerPanel";
-import React, { type ReactElement } from "react";
+import React, { type ReactElement, useCallback } from "react";
function formatSingleDate(
date: DateValue | null,
@@ -32,6 +33,13 @@ function formatSingleDate(
export const SingleWithCustomPanel = (): ReactElement => {
const helperText = "Date format DD MMM YYYY (e.g. 09 Jun 2024)";
const minDate = today(getLocalTimeZone());
+ const handleSelectedDateChange = useCallback(
+ (newSelectedDate: SingleDateSelection | null) => {
+ console.log(`Selected date: ${formatSingleDate(newSelectedDate)}`);
+ },
+ [],
+ );
+
return (
Select a date
@@ -39,9 +47,7 @@ export const SingleWithCustomPanel = (): ReactElement => {
minDate={minDate}
maxDate={minDate.add({ years: 50 })}
selectionVariant="single"
- onSelectedDateChange={(newSelectedDate, _error) => {
- console.log(`Selected date: ${formatSingleDate(newSelectedDate)}`);
- }}
+ onSelectedDateChange={handleSelectedDateChange}
>
diff --git a/site/src/examples/date-picker/SingleWithCustomParser.tsx b/site/src/examples/date-picker/SingleWithCustomParser.tsx
index 1bc1d759f5a..e70736a9600 100644
--- a/site/src/examples/date-picker/SingleWithCustomParser.tsx
+++ b/site/src/examples/date-picker/SingleWithCustomParser.tsx
@@ -20,7 +20,7 @@ import {
getCurrentLocale,
parseCalendarDate,
} from "@salt-ds/lab";
-import { type ReactElement, useState } from "react";
+import { type ReactElement, useCallback, useState } from "react";
function formatSingleDate(
date: DateValue | null,
@@ -34,52 +34,65 @@ function formatSingleDate(
}
export const SingleWithCustomParser = (): ReactElement => {
- const helperText =
+ const defaultHelperText =
"Date format DD MMM YYYY (e.g. 09 Jun 2024) or +/-D (e.g. +7)";
+ const errorHelperText = "Please enter a valid date in DD MMM YYYY format";
+ const [helperText, setHelperText] = useState(defaultHelperText);
const [validationStatus, setValidationStatus] = useState<"error" | undefined>(
undefined,
);
const [selectedDate, setSelectedDate] = useState(
null,
);
+ const handleSelectedDateChange = useCallback(
+ (newSelectedDate: SingleDateSelection | null, error: string | false) => {
+ console.log(`Selected date: ${formatSingleDate(newSelectedDate)}`);
+ setSelectedDate(newSelectedDate);
+ if (error) {
+ setHelperText(errorHelperText);
+ } else {
+ setHelperText(defaultHelperText);
+ }
+ setValidationStatus(error ? "error" : undefined);
+ },
+ [setValidationStatus, setSelectedDate, setHelperText],
+ );
+ const handleParse = useCallback(
+ (inputDate: string): DateInputSingleParserResult => {
+ if (!inputDate?.length) {
+ return { date: null, error: false };
+ }
+ const parsedDate = inputDate;
+ const offsetMatch = parsedDate?.match(/^([+-]?\d+)$/);
+ if (offsetMatch) {
+ const offsetDays = Number.parseInt(offsetMatch[1], 10);
+ let offsetDate = selectedDate
+ ? selectedDate
+ : today(getLocalTimeZone());
+ offsetDate = offsetDate.add({ days: offsetDays });
+ return {
+ date: new CalendarDate(
+ offsetDate.year,
+ offsetDate.month,
+ offsetDate.day,
+ ),
+ error: false,
+ };
+ }
+ return parseCalendarDate(parsedDate || "");
+ },
+ [],
+ );
return (
Select a date
{
- console.log(`Selected date: ${formatSingleDate(newSelectedDate)}`);
- setSelectedDate(newSelectedDate);
- setValidationStatus(error ? "error" : undefined);
- }}
>
- {
- if (!inputDate?.length) {
- return { date: null, error: false };
- }
- const parsedDate = inputDate;
- const offsetMatch = parsedDate?.match(/^([+-]?\d+)$/);
- if (offsetMatch) {
- const offsetDays = Number.parseInt(offsetMatch[1], 10);
- let offsetDate = selectedDate
- ? selectedDate
- : today(getLocalTimeZone());
- offsetDate = offsetDate.add({ days: offsetDays });
- return {
- date: new CalendarDate(
- offsetDate.year,
- offsetDate.month,
- offsetDate.day,
- ),
- error: false,
- };
- }
- return parseCalendarDate(parsedDate || "");
- }}
- />
+
diff --git a/site/src/examples/date-picker/SingleWithFormField.tsx b/site/src/examples/date-picker/SingleWithFormField.tsx
index 1e2170eb627..fabc0baacf7 100644
--- a/site/src/examples/date-picker/SingleWithFormField.tsx
+++ b/site/src/examples/date-picker/SingleWithFormField.tsx
@@ -13,7 +13,7 @@ import {
formatDate,
getCurrentLocale,
} from "@salt-ds/lab";
-import { type ReactElement, useState } from "react";
+import { type ReactElement, useCallback, useState } from "react";
function formatSingleDate(
date: DateValue | null,
@@ -27,12 +27,23 @@ function formatSingleDate(
}
export const SingleWithFormField = (): ReactElement => {
- const helperText = "Date format DD MMM YYYY (e.g. 09 Jun 2024)";
+ const defaultHelperText = "Date format DD MMM YYYY (e.g. 09 Jun 2024)";
+ const errorHelperText = "Please enter a valid date in DD MMM YYYY format";
+ const [helperText, setHelperText] = useState(defaultHelperText);
const [validationStatus, setValidationStatus] = useState<"error" | undefined>(
undefined,
);
- const [selectedDate, setSelectedDate] = useState(
- null,
+ const handleSelectedDateChange = useCallback(
+ (newSelectedDate: SingleDateSelection | null, error: string | false) => {
+ console.log(`Selected date: ${formatSingleDate(newSelectedDate)}`);
+ if (error) {
+ setHelperText(errorHelperText);
+ } else {
+ setHelperText(defaultHelperText);
+ }
+ setValidationStatus(error ? "error" : undefined);
+ },
+ [setValidationStatus, setHelperText],
);
return (
@@ -40,12 +51,7 @@ export const SingleWithFormField = (): ReactElement => {
Select a date
{
- console.log(`Selected date: ${formatSingleDate(newSelectedDate)}`);
- setSelectedDate(newSelectedDate);
- setValidationStatus(error ? "error" : undefined);
- }}
+ onSelectedDateChange={handleSelectedDateChange}
>
diff --git a/site/src/examples/date-picker/SingleWithInitialError.tsx b/site/src/examples/date-picker/SingleWithInitialError.tsx
index 5d592ea344c..3e805599e3e 100644
--- a/site/src/examples/date-picker/SingleWithInitialError.tsx
+++ b/site/src/examples/date-picker/SingleWithInitialError.tsx
@@ -13,7 +13,7 @@ import {
formatDate,
getCurrentLocale,
} from "@salt-ds/lab";
-import { type ReactElement, useState } from "react";
+import { type ReactElement, useCallback, useState } from "react";
function formatSingleDate(
date: DateValue | null,
@@ -27,12 +27,23 @@ function formatSingleDate(
}
export const SingleWithInitialError = (): ReactElement => {
- const helperText = "Date format DD MMM YYYY (e.g. 09 Jun 2024)";
+ const defaultHelperText = "Date format DD MMM YYYY (e.g. 09 Jun 2024)";
+ const errorHelperText = "Please enter a valid date in DD MMM YYYY format";
+ const [helperText, setHelperText] = useState(errorHelperText);
const [validationStatus, setValidationStatus] = useState<"error" | undefined>(
"error",
);
- const [selectedDate, setSelectedDate] = useState(
- null,
+ const handleSelectedDateChange = useCallback(
+ (newSelectedDate: SingleDateSelection | null, error: string | false) => {
+ console.log(`Selected date: ${formatSingleDate(newSelectedDate)}`);
+ if (error) {
+ setHelperText(errorHelperText);
+ } else {
+ setHelperText(defaultHelperText);
+ }
+ setValidationStatus(error ? "error" : undefined);
+ },
+ [setValidationStatus, setHelperText],
);
return (
@@ -40,12 +51,7 @@ export const SingleWithInitialError = (): ReactElement => {
Select a date
{
- console.log(`Selected date: ${formatSingleDate(newSelectedDate)}`);
- setSelectedDate(newSelectedDate);
- setValidationStatus(error ? "error" : undefined);
- }}
+ onSelectedDateChange={handleSelectedDateChange}
>
diff --git a/site/src/examples/date-picker/SingleWithLocaleEnUS.tsx b/site/src/examples/date-picker/SingleWithLocaleEnUS.tsx
index 17f0c4410a0..151aa204e58 100644
--- a/site/src/examples/date-picker/SingleWithLocaleEnUS.tsx
+++ b/site/src/examples/date-picker/SingleWithLocaleEnUS.tsx
@@ -1,84 +1,51 @@
-import {
- CalendarDate,
- DateFormatter,
- type DateValue,
- getLocalTimeZone,
-} from "@internationalized/date";
import {
FormField,
FormFieldHelperText as FormHelperText,
FormFieldLabel as FormLabel,
} from "@salt-ds/core";
import {
- type DateInputSingleParserResult,
DatePicker,
DatePickerOverlay,
DatePickerSingleInput,
DatePickerSinglePanel,
type SingleDateSelection,
+ formatDate,
} from "@salt-ds/lab";
-import React, { type ReactElement, useState } from "react";
-
-const parseDateEnUS = (dateString: string): DateInputSingleParserResult => {
- if (!dateString?.length) {
- return { date: null, error: false };
- }
- const dateParts = dateString.match(/^(\d{1,2})\/(\d{1,2})\/(\d{4})$/);
- if (!dateParts) {
- return { date: null, error: "invalid date" };
- }
- const [, month, day, year] = dateParts;
- return {
- date: new CalendarDate(
- Number.parseInt(year, 10),
- Number.parseInt(month, 10),
- Number.parseInt(day, 10),
- ),
- error: false,
- };
-};
-
-const formatDateEnUS = (date: DateValue | null) => {
- return date
- ? new DateFormatter("en-US", {
- day: "2-digit",
- month: "2-digit",
- year: "numeric",
- }).format(date.toDate(getLocalTimeZone()))
- : "";
-};
+import React, { type ReactElement, useCallback, useState } from "react";
export const SingleWithLocaleEnUS = (): ReactElement => {
const locale = "en-US";
- const [selectedDate, setSelectedDate] = useState(
- null,
- );
- const helperText = `Locale ${locale}`;
+ const defaultHelperText = `Locale ${locale}`;
+ const errorHelperText = "Please enter a valid date in DD MMM YYYY format";
+ const [helperText, setHelperText] = useState(defaultHelperText);
const [validationStatus, setValidationStatus] = useState<"error" | undefined>(
undefined,
);
+ const handleSelectedDateChange = useCallback(
+ (newSelectedDate: SingleDateSelection | null, error: string | false) => {
+ console.log(`Selected date: ${newSelectedDate}`);
+ if (error) {
+ setHelperText(errorHelperText);
+ } else {
+ setHelperText(defaultHelperText);
+ }
+ setValidationStatus(error ? "error" : undefined);
+ },
+ [setValidationStatus, setHelperText],
+ );
return (
Select a date
{
- console.log(`Selected date: ${formatDateEnUS(newSelectedDate)}`);
- setValidationStatus(error ? "error" : undefined);
- setSelectedDate(newSelectedDate);
- }}
+ onSelectedDateChange={handleSelectedDateChange}
>
-
+
-
+
{helperText}
diff --git a/site/src/examples/date-picker/SingleWithLocaleZhCN.tsx b/site/src/examples/date-picker/SingleWithLocaleZhCN.tsx
index 1566f9d03fe..7b9c17bdcc7 100644
--- a/site/src/examples/date-picker/SingleWithLocaleZhCN.tsx
+++ b/site/src/examples/date-picker/SingleWithLocaleZhCN.tsx
@@ -1,5 +1,4 @@
import {
- CalendarDate,
DateFormatter,
type DateValue,
getLocalTimeZone,
@@ -10,7 +9,6 @@ import {
FormFieldLabel as FormLabel,
} from "@salt-ds/core";
import {
- type DateInputSingleParserResult,
DatePicker,
DatePickerOverlay,
DatePickerSingleInput,
@@ -19,47 +17,30 @@ import {
type SingleDateSelection,
formatDate,
} from "@salt-ds/lab";
-import React, { type ReactElement, useState } from "react";
-
-const formatDateZhCN = (date: DateValue | null) => {
- return date
- ? new DateFormatter("zh-CN", {
- day: "2-digit",
- month: "2-digit",
- year: "numeric",
- }).format(date.toDate(getLocalTimeZone()))
- : "";
-};
-
-const parseDateZhCN = (dateString: string): DateInputSingleParserResult => {
- if (!dateString?.length) {
- return { date: null, error: false };
- }
- const dateParts = dateString.match(/^(\d{4})\/(\d{1,2})\/(\d{1,2})$/);
- if (!dateParts) {
- return { date: null, error: "invalid date" };
- }
- const [_, year, month, day] = dateParts;
- return {
- date: new CalendarDate(
- Number.parseInt(year, 10),
- Number.parseInt(month, 10),
- Number.parseInt(day, 10),
- ),
- error: false,
- };
-};
+import React, { type ReactElement, useCallback, useState } from "react";
export const SingleWithLocaleZhCN = (): ReactElement => {
const locale = "zh-CN";
- const [selectedDate, setSelectedDate] = useState(
- null,
- );
- const helperText = `Locale ${locale}`;
+ const defaultHelperText = `Locale ${locale}`;
+ const errorHelperText = "Please enter a valid date in DD MMM YYYY format";
+ const [helperText, setHelperText] = useState(defaultHelperText);
const [validationStatus, setValidationStatus] = useState<"error" | undefined>(
undefined,
);
+ const handleSelectedDateChange = useCallback(
+ (newSelectedDate: SingleDateSelection | null, error: string | false) => {
+ console.log(`Selected date: ${newSelectedDate ?? null}`);
+ if (error) {
+ setHelperText(errorHelperText);
+ } else {
+ setHelperText(defaultHelperText);
+ }
+ setValidationStatus(error ? "error" : undefined);
+ },
+ [setValidationStatus, setHelperText],
+ );
+
const formatMonth = (date: DateValue) =>
formatDate(date, locale, {
month: "long",
@@ -77,27 +58,16 @@ export const SingleWithLocaleZhCN = (): ReactElement => {
Select a date
{
- console.log(
- `Selected date: ${formatDateZhCN(newSelectedDate ?? null)}`,
- );
- setSelectedDate(newSelectedDate);
- setValidationStatus(error ? "error" : undefined);
- }}
+ onSelectedDateChange={handleSelectedDateChange}
>
-
+
({ renderDayContents }),
+ }}
CalendarNavigationProps={{ formatMonth }}
/>
diff --git a/site/src/examples/date-picker/SingleWithMinMaxDate.tsx b/site/src/examples/date-picker/SingleWithMinMaxDate.tsx
index f1378417ecd..b684158e0bd 100644
--- a/site/src/examples/date-picker/SingleWithMinMaxDate.tsx
+++ b/site/src/examples/date-picker/SingleWithMinMaxDate.tsx
@@ -13,7 +13,7 @@ import {
formatDate,
getCurrentLocale,
} from "@salt-ds/lab";
-import { type ReactElement, useState } from "react";
+import { type ReactElement, useCallback, useState } from "react";
function formatSingleDate(
date: DateValue | null,
@@ -27,22 +27,22 @@ function formatSingleDate(
}
export const SingleWithMinMaxDate = (): ReactElement => {
- const [selectedDate, setSelectedDate] = useState(
- null,
+ const helperText = "Select date between 15/01/2030 and 15/01/2031";
+ const handleSelectedDateChange = useCallback(
+ (newSelectedDate: SingleDateSelection | null) => {
+ console.log(`Selected date: ${formatSingleDate(newSelectedDate)}`);
+ },
+ [],
);
- const helperText = "Valid between 15/01/2030 and 15/01/2031";
+
return (
Select a date
{
- console.log(`Selected date: ${formatSingleDate(newSelectedDate)}`);
- setSelectedDate(newSelectedDate);
- }}
minDate={new CalendarDate(2030, 1, 15)}
maxDate={new CalendarDate(2031, 1, 15)}
+ onSelectedDateChange={handleSelectedDateChange}
>
diff --git a/site/src/examples/date-picker/SingleWithToday.tsx b/site/src/examples/date-picker/SingleWithTodayButton.tsx
similarity index 58%
rename from site/src/examples/date-picker/SingleWithToday.tsx
rename to site/src/examples/date-picker/SingleWithTodayButton.tsx
index e0debd29dee..2909457f09b 100644
--- a/site/src/examples/date-picker/SingleWithToday.tsx
+++ b/site/src/examples/date-picker/SingleWithTodayButton.tsx
@@ -5,6 +5,7 @@ import {
} from "@internationalized/date";
import {
Button,
+ Divider,
FlexItem,
FlexLayout,
FormField,
@@ -17,11 +18,12 @@ import {
DatePickerSingleInput,
DatePickerSinglePanel,
type SingleDatePickerState,
+ type SingleDateSelection,
formatDate,
getCurrentLocale,
useDatePickerContext,
} from "@salt-ds/lab";
-import React, { type ReactElement } from "react";
+import React, { type ReactElement, useCallback } from "react";
const TodayButton = () => {
const {
@@ -29,14 +31,17 @@ const TodayButton = () => {
} = useDatePickerContext({
selectionVariant: "single",
}) as SingleDatePickerState;
-
return (
- setSelectedDate(today(getLocalTimeZone()), false)}
- >
- Today
-
+
+ setSelectedDate(today(getLocalTimeZone()), false)}
+ >
+ Select Today
+
+
);
};
@@ -51,23 +56,39 @@ function formatSingleDate(
return date;
}
-export const SingleWithToday = (): ReactElement => {
+export const SingleWithTodayButton = (): ReactElement => {
const helperText = "Date format DD MMM YYYY (e.g. 09 Jun 2024)";
+ const handleSelectedDateChange = useCallback(
+ (newSelectedDate: SingleDateSelection | null) => {
+ console.log(`Selected date: ${formatSingleDate(newSelectedDate)}`);
+ },
+ [],
+ );
+
return (
Select a date
{
- console.log(`Selected date: ${formatSingleDate(newSelectedDate)}`);
- }}
+ onSelectedDateChange={handleSelectedDateChange}
>
+
+
+ {helperText}
+
+
+
+
+
+
+
+
diff --git a/site/src/examples/date-picker/index.ts b/site/src/examples/date-picker/index.ts
index b53e578e12f..38d075f5ff2 100644
--- a/site/src/examples/date-picker/index.ts
+++ b/site/src/examples/date-picker/index.ts
@@ -8,7 +8,7 @@ export * from "./SingleWithLocaleEnUS";
export * from "./SingleWithLocaleZhCN";
export * from "./SingleWithMinMaxDate";
export * from "./SingleWithFormField";
-export * from "./SingleWithToday";
+export * from "./SingleWithTodayButton";
export * from "./SingleBordered";
export * from "./Range";
export * from "./RangeControlled";