diff --git a/.changeset/spicy-islands-drum.md b/.changeset/spicy-islands-drum.md new file mode 100644 index 0000000000..4ef2e10ba1 --- /dev/null +++ b/.changeset/spicy-islands-drum.md @@ -0,0 +1,6 @@ +--- +"@nextui-org/date-input": patch +"@nextui-org/date-picker": patch +--- + +chore(date): update errorMessageFunction story and docs for date libraries diff --git a/apps/docs/content/components/date-input/error-message-function.ts b/apps/docs/content/components/date-input/error-message-function.ts new file mode 100644 index 0000000000..35371af420 --- /dev/null +++ b/apps/docs/content/components/date-input/error-message-function.ts @@ -0,0 +1,30 @@ +const App = `import {DateInput} from "@nextui-org/react"; +import {CalendarDate, parseDate} from "@internationalized/date"; + +export default function App() { + return ( +
+ { + if (value.isInvalid) { + return "Please enter a valid date."; + } + }} + className="max-w-xs" + /> +
+ ); +}`; + +const react = { + "/App.jsx": App, +}; + +export default { + ...react, +}; diff --git a/apps/docs/content/components/date-input/index.ts b/apps/docs/content/components/date-input/index.ts index 663e66615c..3af95ddc94 100644 --- a/apps/docs/content/components/date-input/index.ts +++ b/apps/docs/content/components/date-input/index.ts @@ -7,6 +7,7 @@ import labelPlacements from "./label-placements"; import description from "./description"; import startEndContent from "./start-end-content"; import errorMessage from "./error-message"; +import errorMessageFunction from "./error-message-function"; import controlled from "./controlled"; import timeZones from "./time-zones"; import granularity from "./granularity"; @@ -25,6 +26,7 @@ export const dateInputContent = { description, startEndContent, errorMessage, + errorMessageFunction, controlled, timeZones, granularity, diff --git a/apps/docs/content/components/date-picker/error-message-function.ts b/apps/docs/content/components/date-picker/error-message-function.ts new file mode 100644 index 0000000000..2b473c9f5d --- /dev/null +++ b/apps/docs/content/components/date-picker/error-message-function.ts @@ -0,0 +1,26 @@ +const App = `import {DatePicker} from "@nextui-org/react"; + +export default function App() { + return ( +
+ { + if (value.isInvalid) { + return "Please enter a valid date."; + } + }} + /> +
+ ); +}`; + +const react = { + "/App.jsx": App, +}; + +export default { + ...react, +}; diff --git a/apps/docs/content/components/date-picker/index.ts b/apps/docs/content/components/date-picker/index.ts index f46ee40879..d99bfc245f 100644 --- a/apps/docs/content/components/date-picker/index.ts +++ b/apps/docs/content/components/date-picker/index.ts @@ -6,6 +6,7 @@ import variants from "./variants"; import labelPlacements from "./label-placements"; import description from "./description"; import errorMessage from "./error-message"; +import errorMessageFunction from "./error-message-function"; import withMonthAndYearPickers from "./with-month-and-year-pickers"; import withTimeField from "./with-time-field"; import selectorIcon from "./selector-icon"; @@ -28,6 +29,7 @@ export const datePickerContent = { labelPlacements, description, errorMessage, + errorMessageFunction, withMonthAndYearPickers, withTimeField, selectorIcon, diff --git a/apps/docs/content/components/date-range-picker/error-message-function.ts b/apps/docs/content/components/date-range-picker/error-message-function.ts new file mode 100644 index 0000000000..9e8cb16eab --- /dev/null +++ b/apps/docs/content/components/date-range-picker/error-message-function.ts @@ -0,0 +1,30 @@ +const App = `import {DateRangePicker} from "@nextui-org/react"; +import {parseDate} from "@internationalized/date"; + +export default function App() { + return ( + { + if (value.isInvalid) { + return "Please enter your stay duration"; + } + }} + defaultValue={{ + start: parseDate("2024-04-01"), + end: parseDate("2024-04-08"), + }} + className="max-w-xs" + /> + ); +}`; + +const react = { + "/App.jsx": App, +}; + +export default { + ...react, +}; diff --git a/apps/docs/content/components/date-range-picker/index.ts b/apps/docs/content/components/date-range-picker/index.ts index b4496b2651..27759f8408 100644 --- a/apps/docs/content/components/date-range-picker/index.ts +++ b/apps/docs/content/components/date-range-picker/index.ts @@ -6,6 +6,7 @@ import variants from "./variants"; import labelPlacements from "./label-placements"; import description from "./description"; import errorMessage from "./error-message"; +import errorMessageFunction from "./error-message-function"; import withTimeField from "./with-time-field"; import selectorIcon from "./selector-icon"; import controlled from "./controlled"; @@ -28,6 +29,7 @@ export const dateRangePickerContent = { labelPlacements, description, errorMessage, + errorMessageFunction, withTimeField, selectorIcon, controlled, diff --git a/apps/docs/content/components/time-input/error-message-function.ts b/apps/docs/content/components/time-input/error-message-function.ts new file mode 100644 index 0000000000..b8111e5265 --- /dev/null +++ b/apps/docs/content/components/time-input/error-message-function.ts @@ -0,0 +1,23 @@ +const App = `import {TimeInput} from "@nextui-org/react"; + +export default function App() { + return ( + { + if (value.isInvalid) { + return "Please enter a valid time"; + } + }} + /> + ); +}`; + +const react = { + "/App.jsx": App, +}; + +export default { + ...react, +}; diff --git a/apps/docs/content/components/time-input/error-message.ts b/apps/docs/content/components/time-input/error-message.ts new file mode 100644 index 0000000000..8d60a11647 --- /dev/null +++ b/apps/docs/content/components/time-input/error-message.ts @@ -0,0 +1,19 @@ +const App = `import {TimeInput} from "@nextui-org/react"; + +export default function App() { + return ( + + ); +}`; + +const react = { + "/App.jsx": App, +}; + +export default { + ...react, +}; diff --git a/apps/docs/content/components/time-input/index.ts b/apps/docs/content/components/time-input/index.ts index de077dbed2..9daed76c58 100644 --- a/apps/docs/content/components/time-input/index.ts +++ b/apps/docs/content/components/time-input/index.ts @@ -4,6 +4,8 @@ import disabled from "./disabled"; import readonly from "./read-only"; import withoutLabel from "./without-label"; import withDescription from "./with-description"; +import errorMessage from "./error-message"; +import errorMessageFunction from "./error-message-function"; import labelPlacement from "./label-placement"; import startContent from "./start-content"; import endContent from "./end-content"; @@ -23,6 +25,8 @@ export const timeInputContent = { readonly, withoutLabel, withDescription, + errorMessage, + errorMessageFunction, labelPlacement, startContent, endContent, diff --git a/apps/docs/content/docs/components/date-input.mdx b/apps/docs/content/docs/components/date-input.mdx index df9e377e38..f941ddfb07 100644 --- a/apps/docs/content/docs/components/date-input.mdx +++ b/apps/docs/content/docs/components/date-input.mdx @@ -84,6 +84,10 @@ You can combine the `isInvalid` and `errorMessage` properties to show an invalid +You can also pass an error message as a function. This allows for dynamic error message handling based on the [ValidationResult]((https://github.com/adobe/react-spectrum/blob/1cacbf1d438675feb3859fee54b17e620b458d9c/packages/%40react-types/shared/src/inputs.d.ts#L44-L51)). + + + ### Controlled You can use the `value` and `onChange` properties to control the input value. diff --git a/apps/docs/content/docs/components/date-picker.mdx b/apps/docs/content/docs/components/date-picker.mdx index 9bfd9cc04a..8c9a510980 100644 --- a/apps/docs/content/docs/components/date-picker.mdx +++ b/apps/docs/content/docs/components/date-picker.mdx @@ -77,6 +77,10 @@ You can combine the `isInvalid` and `errorMessage` properties to show an invalid +You can also pass an error message as a function. This allows for dynamic error message handling based on the [ValidationResult]((https://github.com/adobe/react-spectrum/blob/1cacbf1d438675feb3859fee54b17e620b458d9c/packages/%40react-types/shared/src/inputs.d.ts#L44-L51)). + + + ### With Month and Year Pickers diff --git a/apps/docs/content/docs/components/date-range-picker.mdx b/apps/docs/content/docs/components/date-range-picker.mdx index 5a25613c8f..b03e2d6245 100644 --- a/apps/docs/content/docs/components/date-range-picker.mdx +++ b/apps/docs/content/docs/components/date-range-picker.mdx @@ -92,6 +92,10 @@ You can combine the `isInvalid` and `errorMessage` properties to show an invalid +You can also pass an error message as a function. This allows for dynamic error message handling based on the [ValidationResult]((https://github.com/adobe/react-spectrum/blob/1cacbf1d438675feb3859fee54b17e620b458d9c/packages/%40react-types/shared/src/inputs.d.ts#L44-L51)). + + + ### With Time Fields DateRangePicker automatically includes time fields when a `CalendarDateTime` or `ZonedDateTime` object is provided as the value. diff --git a/apps/docs/content/docs/components/time-input.mdx b/apps/docs/content/docs/components/time-input.mdx index 1010112b7d..a6b75e0886 100644 --- a/apps/docs/content/docs/components/time-input.mdx +++ b/apps/docs/content/docs/components/time-input.mdx @@ -78,6 +78,17 @@ A description for the field. Provides a hint such as specific requirements for w +### With Error Message + +You can combine the `isInvalid` and `errorMessage` properties to show an invalid input. + + + +You can also pass an error message as a function. This allows for dynamic error message handling based on the [ValidationResult]((https://github.com/adobe/react-spectrum/blob/1cacbf1d438675feb3859fee54b17e620b458d9c/packages/%40react-types/shared/src/inputs.d.ts#L44-L51)). + + + + ### Label Placement The label's overall position relative to the element it is labeling. diff --git a/packages/components/date-input/src/date-input-group.tsx b/packages/components/date-input/src/date-input-group.tsx index cecd813fdf..998d166e47 100644 --- a/packages/components/date-input/src/date-input-group.tsx +++ b/packages/components/date-input/src/date-input-group.tsx @@ -1,21 +1,11 @@ import type {HTMLAttributes, ReactElement, ReactNode} from "react"; -import type {GroupDOMAttributes} from "@react-types/shared"; +import type {GroupDOMAttributes, HelpTextProps, ValidationResult} from "@react-types/shared"; import {useMemo} from "react"; import {forwardRef} from "@nextui-org/system"; import {dataAttr} from "@nextui-org/shared-utils"; -// TODO: Use HelpTextProps from "@react-types/shared"; once we upgrade react-aria packages to the latest version. -export interface ValidationResult { - /** Whether the input value is invalid. */ - isInvalid: boolean; - /** The current error messages for the input if it is invalid, otherwise an empty array. */ - validationErrors: string[]; - /** The native validation details for the input. */ - validationDetails: ValidityState; -} - -export interface DateInputGroupProps extends ValidationResult { +export interface DateInputGroupProps extends ValidationResult, HelpTextProps { children?: ReactElement | ReactElement[]; shouldLabelBeOutside?: boolean; label?: ReactNode; @@ -27,10 +17,6 @@ export interface DateInputGroupProps extends ValidationResult { labelProps?: HTMLAttributes; descriptionProps?: HTMLAttributes; errorMessageProps?: HTMLAttributes; - /** A description for the field. Provides a hint such as specific requirements for what to choose. */ - description?: ReactNode; - /** An error message for the field. */ - errorMessage?: ReactNode | ((v: ValidationResult) => ReactNode); } export const DateInputGroup = forwardRef<"div", DateInputGroupProps>((props, ref) => { diff --git a/packages/components/date-input/stories/date-input.stories.tsx b/packages/components/date-input/stories/date-input.stories.tsx index 87c2b74697..af0cc936f5 100644 --- a/packages/components/date-input/stories/date-input.stories.tsx +++ b/packages/components/date-input/stories/date-input.stories.tsx @@ -13,6 +13,7 @@ import { } from "@internationalized/date"; import {CalendarBoldIcon} from "@nextui-org/shared-icons"; import {useDateFormatter, I18nProvider} from "@react-aria/i18n"; +import {ValidationResult} from "@react-types/shared"; import {DateInput, DateInputProps} from "../src"; @@ -254,10 +255,25 @@ export const WithErrorMessage = { args: { ...defaultProps, + isInvalid: true, errorMessage: "Please enter a valid date", }, }; +export const WithErrorMessageFunction = { + render: Template, + + args: { + ...defaultProps, + isInvalid: true, + errorMessage: (value: ValidationResult) => { + if (value.isInvalid) { + return "Please enter a valid date"; + } + }, + }, +}; + export const IsInvalid = { render: Template, diff --git a/packages/components/date-input/stories/time-input.stories.tsx b/packages/components/date-input/stories/time-input.stories.tsx index 16f0190bf2..cdf090c8b1 100644 --- a/packages/components/date-input/stories/time-input.stories.tsx +++ b/packages/components/date-input/stories/time-input.stories.tsx @@ -9,6 +9,7 @@ import { ZonedDateTime, } from "@internationalized/date"; import {useDateFormatter} from "@react-aria/i18n"; +import {ValidationResult} from "@react-types/shared"; import {TimeInput, TimeInputProps, TimeInputValue as TimeValue} from "../src"; @@ -192,6 +193,30 @@ export const WithDescription = { }, }; +export const WithErrorMessage = { + render: Template, + + args: { + ...defaultProps, + isInvalid: true, + errorMessage: "Please enter a valid time", + }, +}; + +export const WithErrorMessageFunction = { + render: Template, + + args: { + ...defaultProps, + isInvalid: true, + errorMessage: (value: ValidationResult) => { + if (value.isInvalid) { + return "Please enter a valid date"; + } + }, + }, +}; + export const LabelPlacement = { render: LabelPlacementTemplate, diff --git a/packages/components/date-picker/stories/date-picker.stories.tsx b/packages/components/date-picker/stories/date-picker.stories.tsx index f121153be2..d662a5415e 100644 --- a/packages/components/date-picker/stories/date-picker.stories.tsx +++ b/packages/components/date-picker/stories/date-picker.stories.tsx @@ -17,6 +17,7 @@ import {I18nProvider, useDateFormatter, useLocale} from "@react-aria/i18n"; import {Button, ButtonGroup} from "@nextui-org/button"; import {Radio, RadioGroup} from "@nextui-org/radio"; import {cn} from "@nextui-org/theme"; +import {ValidationResult} from "@react-types/shared"; import {DatePicker, DatePickerProps} from "../src"; @@ -424,10 +425,25 @@ export const WithErrorMessage = { args: { ...defaultProps, + isInvalid: true, errorMessage: "Please enter a valid date", }, }; +export const WithErrorMessageFunction = { + render: Template, + + args: { + ...defaultProps, + isInvalid: true, + errorMessage: (value: ValidationResult) => { + if (value.isInvalid) { + return "Please enter a valid date"; + } + }, + }, +}; + export const IsInvalid = { render: Template, diff --git a/packages/components/date-picker/stories/date-range-picker.stories.tsx b/packages/components/date-picker/stories/date-range-picker.stories.tsx index f24d1d4385..25df66ab0c 100644 --- a/packages/components/date-picker/stories/date-range-picker.stories.tsx +++ b/packages/components/date-picker/stories/date-range-picker.stories.tsx @@ -13,7 +13,7 @@ import { startOfWeek, today, } from "@internationalized/date"; -import {RangeValue} from "@react-types/shared"; +import {RangeValue, ValidationResult} from "@react-types/shared"; import {DateValue} from "@react-types/datepicker"; import {I18nProvider, useDateFormatter, useLocale} from "@react-aria/i18n"; import {Button, ButtonGroup} from "@nextui-org/button"; @@ -499,7 +499,22 @@ export const WithErrorMessage = { args: { ...defaultProps, - errorMessage: "Please enter your stay duration", + isInvalid: true, + errorMessage: "Please enter a valid date range", + }, +}; + +export const WithErrorMessageFunction = { + render: Template, + + args: { + ...defaultProps, + isInvalid: true, + errorMessage: (value: ValidationResult) => { + if (value.isInvalid) { + return "Please enter a valid date range"; + } + }, }, };