import { OntarioDateInput } from '@ongov/ontario-design-system-component-library-react';
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';

# ontario-date-input

Use this component for user-friendly date input and display.

## Usage guidance

Please refer to the [Ontario Design System](https://designsystem.ontario.ca/components/detail/dates.html) for current documentation guidance.

### Disabled and read-only states

This component intentionally does not provide `readOnly` or `disabled` props.

Disabling form controls can create accessibility and usability barriers, and often does not explain what the user needs to fix.

Instead:

- keep controls and submission actions available
- use validation and error messaging to clearly identify missing or invalid input

For implementation examples, see [Error messaging](#error-messaging).

Source: https://designsystem.ontario.ca/components/detail/buttons.html#disabled-buttons

## Configuration

Once the component package has been installed (see Ontario Design System Component Library for installation instructions), the date input component can be added directly into the project's code, and can be customized by updating the properties outlined [here](#properties). Additional information on custom types for the date input properties are outlined [here](#custom-property-types). Please see the [examples](#examples) below for how to configure the component.

## Examples

Example of date input component

```mdx-code-block
<Tabs
	defaultValue="html"
	values={[
		{label: 'HTML', value: 'html'},
		{label: 'React', value: 'react'},
		{label: 'Angular', value: 'angular'},
	]}
	groupId="framework"
	queryString="framework">
<TabItem value="html">
```

```html
<ontario-date-input
	placeholder='{
		"day": "DD",
		"month": "MM",
		"year": "YYYY"
	}'
	min-year="500"
	max-year="1000"
	required="true"
	date-options='["day", "month", "year"]'
	hint-text="For example 2000 03 01"
	caption='{
		"captionText": "Exact Date",
		"captionType": "default"
	}'
></ontario-date-input>
```

```mdx-code-block
</TabItem>
<TabItem value="react">
```

```tsx
<OntarioDateInput
	placeholder={{
		day: 'DD',
		month: 'MM',
		year: 'YYYY',
	}}
	minYear={500}
	maxYear={1000}
	required={true}
	dateOptions={['day', 'month', 'year']}
	hintText="For example 2000 03 01"
	caption={{
		captionText: 'Exact Date',
		captionType: 'default',
	}}
/>
```

```mdx-code-block
</TabItem>
<TabItem value="angular">
```

```html
<ontario-date-input
	[placeholder]="{ day: 'DD', month: 'MM', year: 'YYYY' }"
	[minYear]="500"
	[maxYear]="1000"
	required="true"
	[dateOptions]="['day', 'month', 'year']"
	[hintText]="'For example 2000 03 01'"
	[caption]="{ captionText: 'Exact Date', captionType: 'default' }"
></ontario-date-input>
```

```mdx-code-block
</TabItem>
</Tabs>
```

<div>
	<OntarioDateInput
		placeholder={{
			day: 'DD',
			month: 'MM',
			year: 'YYYY',
		}}
		minYear={500}
		maxYear={1000}
		required={true}
		dateOptions={['day', 'month', 'year']}
		hintText="For example 2000 03 01"
		caption={{
			captionText: 'Exact Date',
			captionType: 'default',
		}}
	></OntarioDateInput>
</div>

Example of passing custom date validation function to modify validation logic or providing custom error strings.

```html
<ontario-date-input></ontario-date-input>

<script>
	function handleDateValidation(day, month, year) {
		let error = '';
		let dayInvalid = false;
		let monthInvalid = false;
		let yearInvalid = false;

		if (day <= 0) {
			dayInvalid = true;
		}

		if (month <= 0) {
			monthInvalid = true;
		}

		if (year <= 0) {
			yearInvalid = true;
		}

		if (dayInvalid || monthInvalid || yearInvalid) {
			error = 'Invalid date.';
		}

		// add more logic here

		return { errorMessage: error, dayInvalid, monthInvalid, yearInvalid };
	}

	document.querySelector('ontario-date-input').dateValidator = handleDateValidation;
</script>
```

Example of prefilling the component with an aggregate value for edit or resume flows.

```mdx-code-block
<Tabs
	defaultValue="html"
	values={[
		{label: 'HTML', value: 'html'},
		{label: 'React', value: 'react'},
		{label: 'Angular', value: 'angular'},
	]}
	groupId="framework"
	queryString="framework">
<TabItem value="html">
```

```html
<ontario-date-input value="2024-02-20" caption="Exact date" hint-text="For example 2000 03 01"></ontario-date-input>
```

```mdx-code-block
</TabItem>
<TabItem value="react">
```

```tsx
<OntarioDateInput value="2024-02-20" caption="Exact date" hintText="For example 2000 03 01" />
```

```mdx-code-block
</TabItem>
<TabItem value="angular">
```

```html
<ontario-date-input
	[value]="'2024-02-20'"
	[caption]="'Exact date'"
	[hintText]="'For example 2000 03 01'"
></ontario-date-input>
```

```mdx-code-block
</TabItem>
</Tabs>
```

The `value` prop accepts either:

- a plain ISO date such as `2024-02-20`
- a full ISO 8601 timestamp such as `2024-02-20T15:30:00.000Z`

When a valid `value` is provided, the component hydrates the internal year, month, and day fields and normalizes its aggregate value to a full UTC ISO timestamp.

When the aggregate value changes, listen for the component `input` and
`change` events and read the normalized value from `event.target.value`,
similar to a native form control. For convenience, the same aggregate value is
also available in `event.detail.value`. The existing `inputOnInput` and
`inputOnChange` custom events remain available if you need the field-level
detail.

### Forms

The `ontario-date-input` supports integration with native HTML `<form>` elements. This element integrates with the underlying browser form API.

The `ontario-date-input` returns an [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) formatted date with a time component of `00:00:00Z`, or midnight UTC, eg. `2024-02-20T00:00:00.000Z`, combining the _year_, _month_, and _day_ fields together. _It has to be noted that the input only supports numerical values within any of the fields._

The aggregate `value` property on the component follows the same normalized
full ISO format. This means:

- incoming plain ISO dates are accepted and normalized
- incoming full ISO timestamps are accepted and normalized to midnight UTC for the same calendar date
- incomplete or invalid field combinations do not produce an aggregate value for form submission

If you are listening for changes on the component, read the normalized
aggregate value from the component instance:

```js
document.querySelector('ontario-date-input').addEventListener('change', (event) => {
	console.log(event.target.value);
	console.log(event.detail.value);
});
```

The same pattern works in framework wrappers:

```mdx-code-block
<Tabs
groupId="framework-events"
defaultValue="react"
values={[
{label: 'React', value: 'react'},
{label: 'Angular', value: 'angular'},
]}>
<TabItem value="react">
```

```tsx
<OntarioDateInput
	value="2024-02-20"
	caption="Exact date"
	onInput={(event) => {
		console.log((event.target as HTMLOntarioDateInputElement).value);
	}}
	onChange={(event) => {
		console.log((event.target as HTMLOntarioDateInputElement).value);
	}}
/>
```

```mdx-code-block
</TabItem>
<TabItem value="angular">
```

```html
<ontario-date-input
	[value]="'2024-02-20'"
	[caption]="'Exact date'"
	(input)="handleDateInput($event)"
	(change)="handleDateChange($event)"
></ontario-date-input>
```

```ts
handleDateInput(event: Event) {
	console.log((event.target as HTMLOntarioDateInputElement).value);
}

handleDateChange(event: Event) {
	console.log((event.target as HTMLOntarioDateInputElement).value);
}
```

```mdx-code-block
</TabItem>
</Tabs>
```

```html
<form>
	<!-- Add an ontario-date-input -->
	<ontario-date-input
		element-id="date-input-1"
		name="date-input-1"
		placeholder='{
			"day": "DD",
			"month": "MM",
			"year": "YYYY"
		}'
		min-year="999"
		max-year="9999"
		required
		date-options='["day", "month", "year"]'
		hint-text="For example 2000 03 01"
		caption="Enter a date"
	></ontario-date-input>

	<!-- Submit the form with a submit button -->
	<ontario-button type="primary" html-type="submit">Submit</ontario-button>
</form>
```

Remember to set the `name` attribute as this is used to identify the field when submitting the form.

## Event model

Each event emitted by the component uses the [`CustomEvent`](https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent) type to emit a custom event to help communicate what the component is doing. To access the data emitted by the component within the `CustomEvent` type use the [CustomEvent.detail](https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/detail) property.

Eg. To access the value of any change made to this component from the `inputOnInput` event, use the following code to wire up to listen for the the `inputOnInput` event.

```html
<ontario-date-input
	element-id="date-input-example-id"
	id="date-input-1"
	placeholder='{
		"day": "DD",
		"month": "MM",
		"year": "YYYY"
	}'
	min-year="999"
	max-year="9999"
	required="true"
	date-options='["day", "month", "year"]'
	hint-text="For example 2000 03 01"
	caption='{
		"captionText": "Exact Date",
		"captionType": "default"
	}'
></ontario-date-input>
<script>
	// Note: this waits for the page and components to load before
	// locating the component.
	window.onload = () => {
		const dateInput1 = document.getElementById('date-input-1');
		dateInput1.addEventListener('inputOnInput', (event) => {
			console.log('OnInput detail:', event.detail);
		});
	};
</script>
```

If the number `2` is entered into the year input within `date-input-1`, the value of `event.detail` is the object emitted along with the `inputOnInput` event.

```js
{ value: "2", fieldType: "year" }
```

See the [Events](#events) table to learn more about the available custom events from the component and what the type of `CustomEvent.detail` will be.

### Native `input` and `change` events

The component uses a ShadowDOM to maintain encapsulation, however, this changes how the events flow from the inside of the component to the outside in the DOM.

The component emits `input` and `change` events when the aggregate date value
changes so consumers can respond to updates using the familiar native event
names. Read the aggregate ISO value from `event.target.value`.

The same aggregate value is also included in `event.detail.value` for consumers that prefer event payloads.

The field-level `inputOnInput` and `inputOnChange` custom events remain available when you need to know which sub-field changed.

When using libraries that listen for events, this process may not work with them and a workaround might be required depending on the framework or library in use.

## Error messaging

Use validation and error messaging to help users understand what needs to be corrected.

### Setting an error message

Use `dateValidator` to return contextual errors (`DateValidatorReturnType`) and surface message text through `errorMessage`.

### Static vs live validation

Use a static `errorMessage` when validation happens on submit (for example, after a form post or submit handler check).

Use live validation when you want real-time feedback as users interact (for example, on change or blur).

For more guidance, visit the [Error messaging guidance page](https://designsystem.ontario.ca/components/detail/error-messaging.html).

```mdx-code-block
<Tabs
	defaultValue="html"
	values={[
		{label: 'HTML', value: 'html'},
		{label: 'React', value: 'react'},
		{label: 'Angular', value: 'angular'},
	]}
	groupId="framework"
	queryString="framework">
<TabItem value="html">
```

```html
<ontario-date-input id="date-of-birth" caption="Date of birth" required></ontario-date-input>
<script>
	window.addEventListener('load', () => {
		const dateInput = document.getElementById('date-of-birth');
		dateInput.dateValidator = (day, month, year) => {
			const allPartsPresent = !!day && !!month && !!year;
			return {
				errorMessage: allPartsPresent ? '' : 'Enter a complete date of birth.',
				dayInvalid: !day,
				monthInvalid: !month,
				yearInvalid: !year,
			};
		};
	});
</script>
```

```mdx-code-block
</TabItem>
<TabItem value="react">
```

```tsx
<OntarioDateInput
	elementId="date-of-birth"
	caption="Date of birth"
	required
	dateValidator={(day, month, year) => {
		const allPartsPresent = !!day && !!month && !!year;
		return {
			errorMessage: allPartsPresent ? '' : 'Enter a complete date of birth.',
			dayInvalid: !day,
			monthInvalid: !month,
			yearInvalid: !year,
		};
	}}
/>
```

```mdx-code-block
</TabItem>
<TabItem value="angular">
```

```html
<ontario-date-input
	[elementId]="'date-of-birth'"
	[caption]="'Date of birth'"
	[required]="true"
	[dateValidator]="validateDateOfBirth"
></ontario-date-input>
```

```mdx-code-block
</TabItem>
</Tabs>
```

### Live validation

Keep the control available and validate date fields on interaction (for example, on blur or submit). Return contextual error guidance from `dateValidator` so users can correct incomplete or invalid values.

## Custom property types

### DateValidatorReturnType

The `dateValidator` function returns `DateValidatorReturnType` for custom date input validation. Below is example of dateValidator function.

```html
<script>
	function handleDateValidation(day, month, year) {
		return {
			errorMessage: 'Invalid Error',
			dayInvalid: true,
			monthInvalid: false,
			yearInvalid: false,
		};
	}
</script>
```

| Property       | Type      | Description                                | Required |
| -------------- | --------- | ------------------------------------------ | -------- |
| `errorMessage` | `string`  | Error message to display for invalid date. | NO       |
| `dayInvalid`   | `boolean` | Indicate if day is invalid                 | YES      |
| `monthInvalid` | `boolean` | Indicate if month is invalid               | YES      |
| `yearInvalid`  | `boolean` | Indicate if year is invalid                | YES      |

### caption

The `caption` property is used to render the label for the ontario-input. It can be passed either a string or an object. If no `captionType` needs to be specified, it can be passed as a string.

```html
caption='{ "captionText": "Exact Date", "captionType": "heading" }'
```

## Technical Note: SSR (Server-Side Rendering) Considerations

The Ontario Date Input component is compatible with server-side rendering, with a few additional considerations:

- **Language prop:** Language change events only fire in the browser after hydration. To ensure the correct language is rendered during SSR, pass the desired `language` explicitly as a prop.
- **Dynamic ID generation:** If `elementId` is not passed, a UUID is generated at runtime. To prevent hydration mismatches between server and client, explicitly pass a stable `elementId`.
- **Rendered structure vs submitted value:** During SSR, the component renders the expected day, month, and year inputs. The aggregated ISO 8601 value is assembled after hydration, so do not assume that aggregate value will participate in native form submission before hydration.
- **Form participation:** This component uses the [Form-Associated Custom Elements](https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals) API (`@AttachInternals`) to participate in native form submission. Full date normalization, aggregate host `input` and `change` events, and component-managed date validation become available after hydration.
- **No-JavaScript fallback:** If a flow must submit before hydration or without JavaScript, provide explicit native fallback markup in the consuming application.
- **Framework guidance:** Use the HTML `<form>` example above for native submit or progressive-enhancement flows once hydrated. For client-managed integrations, use the event examples below. For App Router setup details, follow the [Next.js integration guide](https://designsystem.ontario.ca/developer-docs/framework-integrations/next-js-ssr/).

> **Recommended for SSR:**
>
> ```tsx
> <OntarioDateInput elementId="my-date-input" language="en"></OntarioDateInput>
> ```

<!-- Auto Generated Below -->

## Overview

Ontario Date Input captures day, month, and year values as a single date field.

This component intentionally does not expose `readOnly` or `disabled` props.

To support accessible and understandable form completion:

- keep form fields and submission actions available
- use validation and error messaging to guide corrections

For component guidance, see:

- https://designsystem.ontario.ca/components/detail/dates.html
- https://designsystem.ontario.ca/developer-docs/components/ontario-date-input/

Disabled/read-only policy source:

- https://designsystem.ontario.ca/components/detail/buttons.html#disabled-buttons

## Properties

| Property        | Attribute        | Description                                                                                                                                                                                                                                                                                                             | Type                                                                                                             | Default                    |
| --------------- | ---------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- | -------------------------- |
| `caption`       | `caption`        | The text to display as the input label                                                                                                                                                                                                                                                                                  | `Caption \| string`                                                                                              | `undefined`                |
| `dateOptions`   | `date-options`   | An array value used to display date options. For example, only the day and month fields can be displayed by specifying the dateOptions as `["day", "month"]`, etc. This is optional. If no prop for `dateOptions` is passed, it will default to `["day", "month", "year"]`.                                             | `DateInputFieldType[] \| string \| undefined`                                                                    | `['day', 'month', 'year']` |
| `dateValidator` | `date-validator` | A function used to override internal date validation logic, which takes three arguments (i.e day, month and year) and returns an object of type `DateValidatorReturnType` This is optional. If no prop for `dateValidator` is passed, it will default to internal validation function to validate the date input.       | `((day: string, month: string, year: string) => DateValidatorReturnType) \| undefined`                           | `undefined`                |
| `elementId`     | `element-id`     | The unique identifier of the input. This is optional - if no ID is passed, one will be generated.                                                                                                                                                                                                                       | `string \| undefined`                                                                                            | `undefined`                |
| `hintText`      | `hint-text`      | Used to include the ontario-hint-text component for the date input group. This is optional.                                                                                                                                                                                                                             | `string \| undefined`                                                                                            | `undefined`                |
| `language`      | `language`       | The language of the component. This is used for translations, and is by default set through event listeners checking for a language property from the header. If none are passed, it will default to English.                                                                                                           | `"en" \| "fr" \| undefined`                                                                                      | `undefined`                |
| `maxYear`       | `max-year`       | A number value indicating maximum value allowed for year input field of the date component. This is optional. If no prop is passed, it will default to `9999`.                                                                                                                                                          | `number \| undefined`                                                                                            | `undefined`                |
| `minYear`       | `min-year`       | A number value indicating minimum value allowed for year input field of the date component. This is optional. If no prop is passed, it will default to `999`.                                                                                                                                                           | `number \| undefined`                                                                                            | `undefined`                |
| `placeholder`   | `placeholder`    | An object value used to set the placeholder text for the day, month and year input fields. Any combination of the three input fields (i.e day, month, year) of the date component can be overridden. This is optional. If no prop is passed, it will not display any placeholder text.                                  | `string \| undefined \| { day?: string \| undefined; month?: string \| undefined; year?: string \| undefined; }` | `undefined`                |
| `required`      | `required`       | A boolean value to determine whether or not the date input is required. This is optional. If no prop is passed, it will default to `false`.                                                                                                                                                                             | `boolean \| undefined`                                                                                           | `false`                    |
| `value`         | `value`          | The aggregate date value for the component. Accepts either a plain ISO date (`YYYY-MM-DD`) or a full ISO 8601 timestamp. When a valid value is provided, the component hydrates the internal day, month, and year fields and normalizes the stored form value to a full UTC ISO timestamp (`YYYY-MM-DDT00:00:00.000Z`). | `string \| undefined`                                                                                            | `undefined`                |

## Events

| Event                | Description                                                                | Type                                                                     |
| -------------------- | -------------------------------------------------------------------------- | ------------------------------------------------------------------------ |
| `inputErrorOccurred` | Emitted when an error message is reported to the component.                | `CustomEvent<{ inputId: string; errorMessage: string; }>`                |
| `inputOnBlur`        | Emitted when a keyboard input event occurs when an input has lost focus.   | `CustomEvent<"day" \| "month" \| "year">`                                |
| `inputOnChange`      | Emitted when a `change` event occurs within the component.                 | `CustomEvent<{ value: string; fieldType: "day" \| "month" \| "year"; }>` |
| `inputOnFocus`       | Emitted when a keyboard input event occurs when an input has gained focus. | `CustomEvent<"day" \| "month" \| "year">`                                |
| `inputOnInput`       | Emitted when an `input` event occurs within the component.                 | `CustomEvent<{ value: string; fieldType: "day" \| "month" \| "year"; }>` |

## Dependencies

### Depends on

- [ontario-icon-alert-error](../ontario-icon)

### Graph

```mermaid
graph TD;
  ontario-date-input --> ontario-icon-alert-error
  style ontario-date-input fill:#f9f,stroke:#333,stroke-width:4px
```

---

_Built with [StencilJS](https://stenciljs.com/)_
