Skip to content
Open
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
693df86
feat(antares): add DateField component
egaitan-godaddy May 29, 2026
f17bc91
chore: add changeset for DateField
egaitan-godaddy May 29, 2026
c2dc804
refactor(antares): use Flex for DateField input layout
egaitan-godaddy May 29, 2026
4e4e678
docs(antares): trim DateField docstrings
egaitan-godaddy May 30, 2026
a82bb1e
chore: stop tracking date-components plan and PDR
egaitan-godaddy Jun 1, 2026
8366787
Merge remote-tracking branch 'origin/main' into feat/date-field
egaitan-godaddy Jun 1, 2026
5d7efea
docs(antares): clarify DateField scope and simplify Working-with-dates
egaitan-godaddy Jun 1, 2026
8cac9bc
docs(antares): make DateField the canonical Working-with-dates home
egaitan-godaddy Jun 1, 2026
8d4c966
refactor(antares): trim DateField CSS — drop redundant cursors and --…
egaitan-godaddy Jun 1, 2026
a9f01d9
feat(antares): re-export I18nProvider from DateField
egaitan-godaddy Jun 1, 2026
b0ed64e
docs(antares): clarify date string format in DateField documentation
egaitan-godaddy Jun 1, 2026
ad658b6
style(field-frame): fix typography and layout
egaitan-godaddy Jun 1, 2026
f252ff4
test: update visual regression screenshots
github-actions[bot] Jun 1, 2026
aaab835
test(date-field): enhance visual tests with additional examples for D…
egaitan-godaddy Jun 1, 2026
9eef7e0
test: update visual regression screenshots
github-actions[bot] Jun 1, 2026
df5198f
feat(date-field): add i18n example and update documentation for DateF…
egaitan-godaddy Jun 2, 2026
4dab7d6
Merge branch 'main' into feat/date-field
egaitan-godaddy Jun 2, 2026
255da49
test: update visual regression screenshots
github-actions[bot] Jun 2, 2026
dc23a05
feat(date-field): enhance DateField component with extended props and…
egaitan-godaddy Jun 2, 2026
5d4d14c
refactor(antares): separate I18nProvider export and enhance field foc…
egaitan-godaddy Jun 2, 2026
8ad7bdc
fix(field-frame): update group context handling
egaitan-godaddy Jun 2, 2026
afd3583
feat(antares): changeset description
egaitan-godaddy Jun 2, 2026
e50402b
Merge branch 'main' into feat/date-field
egaitan-godaddy Jun 18, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/date-field-component.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@godaddy/antares': minor
---

feat(antares): add DateField component
Comment thread
egaitan-godaddy marked this conversation as resolved.
Outdated
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,10 @@

.frame {
border-radius: var(--field-frame-br);
min-height: 40.5px;
box-sizing: border-box;
font-family: inherit;
font-size: var(--font-body-size-md, var(--ux-cxbe8g, 1rem));
line-height: var(--ux-jw5s9j, 1.375);

& > :first-child {
border-start-start-radius: var(--field-frame-br);
Expand Down Expand Up @@ -74,6 +76,7 @@
}

.input {
font: inherit;
background: transparent;
border: none;
outline: none;
Expand All @@ -86,10 +89,6 @@
}
}

input.input {
.input {
padding-block: var(--sp-md);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a small difference in the figma design. In padding top/bottom for TextArea is density * 1 and for TextField it is density * 2

Image Image

}
Comment thread
egaitan-godaddy marked this conversation as resolved.
Outdated

textarea.input {
padding-block: var(--sp-sm);
}
132 changes: 132 additions & 0 deletions packages/@godaddy/antares/components/date-field/README.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
---
title: DateField
description: DateField is a segmented date input with editable Year, Month, and Day segments. Use it when users only need to type a date. If you also want a calendar popover, use `DatePicker` (or `DateRangePicker` for ranges) instead.
Comment thread
egaitan-godaddy marked this conversation as resolved.
---
Comment thread
egaitan-godaddy marked this conversation as resolved.

import { ArgTypes, Meta, Source, Story } from '@storybook/addon-docs/blocks';
import * as Stories from './date-field.stories.tsx';

import SourceBasic from './examples/basic.tsx?raw';
import SourceWithDefaultValue from './examples/with-default-value.tsx?raw';
import SourceControlled from './examples/controlled.tsx?raw';
import SourceWithDescription from './examples/with-description.tsx?raw';
import SourceWithError from './examples/with-error.tsx?raw';
import SourceMinMax from './examples/min-max.tsx?raw';
import SourceDisabledRequiredReadOnly from './examples/disabled-required-readonly.tsx?raw';
import SourceForm from './examples/form.tsx?raw';
import SourceWithI18n from './examples/with-i18n.tsx?raw';

<Meta of={Stories} name="Overview" />

## Features

- **Segmented input**: Editable Year, Month, and Day segments with arrow-key adjustment
- **Label, description, error**: Optional label, helper text, and error message with proper accessibility
- **Controlled or uncontrolled**: Use `value` and `onChange`, or `defaultValue`
- **Range bounds**: `minValue` and `maxValue` reject out-of-range entry
- **Form integration**: Submits an ISO date string when given a `name` prop
- **Accessible by default**: Full keyboard support, ARIA semantics, and locale-aware segment handling

## Installation

```bash
npm install --save @godaddy/antares
```

## Working with dates

Date components in Antares are typed for `CalendarDate` from `@internationalized/date` — a
date-only type with no time and no timezone.

### Why `@internationalized/date`?

JavaScript's built-in `Date` is a single timestamp that conflates calendar date, time of day,
and timezone. That's the wrong shape for "a calendar date" (e.g. a birthday or a booking date),
and round-tripping it through string parsers and `toISOString()` is the source of countless
off-by-one timezone bugs. [`@internationalized/date`](https://react-spectrum.adobe.com/internationalized/date/index.html)
separates these concepts cleanly. Antares date components only deal with `CalendarDate`.

### Installation

Install `@internationalized/date` alongside `@godaddy/antares`:

```bash
npm install --save @internationalized/date
```

### Locale and i18n

Locale comes from the host app's `<I18nProvider>`, which Antares exports from
`@godaddy/antares`. If no `<I18nProvider>` is found, the date will be formatted in the default locale.

## Props

The DateField component accepts the following props:

<ArgTypes of={Stories.Props} />

## Examples

### Default

Minimal usage with a label.

<Source language="tsx" code={SourceBasic} />
<Story of={Stories.Default} inline />

### With default value

Pass a `CalendarDate` to `defaultValue` to initialize the segments.

<Source language="tsx" code={SourceWithDefaultValue} />
<Story of={Stories.WithDefaultValue} inline />

### Controlled

Use `value` and `onChange` for controlled state.

<Source language="tsx" code={SourceControlled} />
<Story of={Stories.Controlled} inline />

### With description

Provide helper text via `description`.

<Source language="tsx" code={SourceWithDescription} />
<Story of={Stories.WithDescription} inline />

### With error

Use `isInvalid` with `errorMessage` for validation feedback.

<Source language="tsx" code={SourceWithError} />
<Story of={Stories.WithError} inline />

### Min and max

Constrain typed entry with `minValue` and `maxValue`.

<Source language="tsx" code={SourceMinMax} />
<Story of={Stories.MinMax} inline />

### Disabled, required, read-only

`isDisabled` blocks all interaction. `isRequired` marks the field for form validation. `isReadOnly`
allows focus and copy but blocks edits.

<Source language="tsx" code={SourceDisabledRequiredReadOnly} />
<Story of={Stories.DisabledRequiredReadOnly} inline />

### Form

Pass `name` to integrate with native `<form>` submission. The submitted value is a date string with the format `YYYY-MM-DD`.

<Source language="tsx" code={SourceForm} />
<Story of={Stories.Form} inline />

### With I18nProvider

Wrap the `DateField` in an `<I18nProvider locale="…">` to override the locale used for segment ordering, formatting, and writing direction. Compare an English locale to an RTL Arabic locale below.

<Source language="tsx" code={SourceWithI18n} />
<Story of={Stories.WithI18n} inline />
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
'use client';
import { getComponentDocs, getMeta, getStory } from '@bento/storybook-addon-helpers';
import { DateFieldBasicExample } from './examples/basic.tsx';
import { DateFieldControlledExample } from './examples/controlled.tsx';
import { DateFieldDisabledRequiredReadOnlyExample } from './examples/disabled-required-readonly.tsx';
import { DateFieldFormExample } from './examples/form.tsx';
import { DateFieldMinMaxExample } from './examples/min-max.tsx';
import {
DateFieldPlaygroundExample,
type DateFieldPlaygroundExampleProps
} from './examples/date-field-playground.tsx';
import { DateFieldWithDefaultValueExample } from './examples/with-default-value.tsx';
import { DateFieldWithDescriptionExample } from './examples/with-description.tsx';
import { DateFieldWithErrorExample } from './examples/with-error.tsx';
import { DateFieldWithI18nExample } from './examples/with-i18n.tsx';
import { DateField } from './src/index.tsx';

export default getMeta({
title: 'components/DateField'
});

export const Props = getComponentDocs(DateField);

export const Default = getStory(DateFieldBasicExample);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In other stories file, this is named Basic

Basic = getStory(DateFieldBasicExample)


export const WithDefaultValue = getStory(DateFieldWithDefaultValueExample);

export const Controlled = getStory(DateFieldControlledExample);

export const WithDescription = getStory(DateFieldWithDescriptionExample);

export const WithError = getStory(DateFieldWithErrorExample);

export const MinMax = getStory(DateFieldMinMaxExample);

export const DisabledRequiredReadOnly = getStory(DateFieldDisabledRequiredReadOnlyExample);

export const Form = getStory(DateFieldFormExample);

export const WithI18n = getStory(DateFieldWithI18nExample);

export const Playground = {
render: (args: DateFieldPlaygroundExampleProps) => <DateFieldPlaygroundExample {...args} />,
args: {
label: 'Start date',
isDisabled: false,
isInvalid: false,
isReadOnly: false,
isRequired: false
},
argTypes: {
label: { control: 'text', description: 'Label text shown above the frame' },
description: { control: 'text', description: 'Helper text shown below the frame' },
errorMessage: { control: 'text', description: 'Error message when invalid' },
isDisabled: { control: 'boolean', description: 'Disable the input' },
isInvalid: { control: 'boolean', description: 'Show invalid state' },
isReadOnly: { control: 'boolean', description: 'Make the input read-only' },
isRequired: { control: 'boolean', description: 'Mark as required' }
}
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { DateField, type DateFieldProps } from '@godaddy/antares';

export function DateFieldBasicExample(props: DateFieldProps) {
return <DateField label="Start date" {...props} />;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { type CalendarDate, parseDate } from '@internationalized/date';
import { useState } from 'react';
import { DateField, Text } from '@godaddy/antares';

export function DateFieldControlledExample() {
const [value, setValue] = useState<CalendarDate | null>(parseDate('2024-03-15'));

return (
<>
<DateField label="Start date" value={value} onChange={setValue} />
<Text>
<strong>Value:</strong> {value ? value.toString() : '(empty)'}
</Text>
</>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { DateField } from '@godaddy/antares';

export interface DateFieldPlaygroundExampleProps {
description?: string;
errorMessage?: string;
isDisabled?: boolean;
isInvalid?: boolean;
isReadOnly?: boolean;
isRequired?: boolean;
label?: string;
}

export function DateFieldPlaygroundExample({
description,
errorMessage,
isDisabled,
isInvalid,
isReadOnly,
isRequired,
label
}: DateFieldPlaygroundExampleProps) {
return (
<DateField
description={description}
errorMessage={errorMessage}
isDisabled={isDisabled}
isInvalid={isInvalid}
isReadOnly={isReadOnly}
isRequired={isRequired}
label={label}
/>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { parseDate } from '@internationalized/date';
import { Flex, DateField } from '@godaddy/antares';

export function DateFieldDisabledRequiredReadOnlyExample() {
return (
<Flex direction="column" gap="md">
<DateField label="Disabled" defaultValue={parseDate('2024-03-15')} isDisabled />
<DateField label="Required" isRequired />
<DateField label="Read-only" defaultValue={parseDate('2024-03-15')} isReadOnly />
</Flex>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { type FormEvent, useState } from 'react';
import { Button, DateField, Flex, Text } from '@godaddy/antares';

export function DateFieldFormExample() {
const [submitted, setSubmitted] = useState<string | null>(null);

function handleSubmit(event: FormEvent<HTMLFormElement>) {
event.preventDefault();
const formData = new FormData(event.currentTarget);
setSubmitted(String(formData.get('startDate') ?? ''));
}

return (
<form onSubmit={handleSubmit}>
<Flex direction="column" gap="md" alignItems="flex-start">
<DateField label="Start date" name="startDate" isRequired />
<Button type="submit">Submit</Button>
{submitted !== null && (
<Text>
<strong>Submitted:</strong> {submitted || '(empty)'}
</Text>
)}
</Flex>
</form>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { parseDate } from '@internationalized/date';
import { DateField } from '@godaddy/antares';

export function DateFieldMinMaxExample() {
return (
<DateField
label="Booking date"
description="Must fall within 2024."
minValue={parseDate('2024-01-01')}
maxValue={parseDate('2024-12-31')}
defaultValue={parseDate('2024-06-15')}
/>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { parseDate } from '@internationalized/date';
import { DateField } from '@godaddy/antares';

export function DateFieldWithDefaultValueExample() {
return <DateField label="Start date" defaultValue={parseDate('2024-03-15')} />;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { DateField } from '@godaddy/antares';

export function DateFieldWithDescriptionExample() {
return <DateField label="Start date" description="The date your subscription begins." />;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { DateField } from '@godaddy/antares';

export function DateFieldWithErrorExample() {
return <DateField label="Start date" errorMessage="Please enter a valid date." isInvalid isRequired />;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { parseDate } from '@internationalized/date';
import { DateField, Flex, I18nProvider } from '@godaddy/antares';

export function DateFieldWithI18nExample() {
return (
<Flex direction="column" gap="md">
<I18nProvider locale="en-US">
<DateField label="Start date (en-US)" defaultValue={parseDate('2024-03-15')} />
</I18nProvider>
<I18nProvider locale="ar-AE">
<DateField label="Start date (ar-AE)" defaultValue={parseDate('2024-03-15')} />
</I18nProvider>
</Flex>
);
}
Loading