Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
7 changes: 7 additions & 0 deletions .changeset/datetimeinput-controlled-empty-calendar.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@astryxdesign/core': patch
---

[fix] Keep DateTimeInput's calendar selection empty after clearing the field.

@cixzhang @korkt-kim
38 changes: 38 additions & 0 deletions apps/storybook/stories/DateTimeInput.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import {useState} from 'react';
import type {Meta, StoryObj} from '@storybook/react';
import {expect, userEvent, waitFor, within} from 'storybook/test';
import {DateTimeInput} from '@astryxdesign/core/DateTimeInput';
import type {ISODateTimeString} from '@astryxdesign/core/DateTimeInput';
import {Theme, defineTheme} from '@astryxdesign/core/theme';
Expand Down Expand Up @@ -236,6 +237,43 @@ export const WithClearButton: Story = {
},
};

/**
* Selects a date, clears the controlled value, and reopens the calendar. The
* final frame is the regression endpoint: an empty field with no selected day.
*/
export const ClearedCalendarSelection: Story = {
tags: ['visual-baseline'],
render: args => {
const [value, setValue] = useState<ISODateTimeString | undefined>(
'2026-03-15T09:00' as ISODateTimeString,
);
return <DateTimeInput {...args} value={value} onChange={setValue} />;
},
args: {
label: 'Meeting time',
hasClear: true,
min: '2026-03-01T00:00' as ISODateTimeString,
max: '2026-03-31T23:59' as ISODateTimeString,
presentation: 'popover',
},
play: async ({canvasElement}) => {
const canvas = within(canvasElement);

await userEvent.click(canvas.getByRole('button', {name: 'Open calendar'}));
await userEvent.click(canvas.getByRole('button', {name: /March 20, 2026/}));
await userEvent.click(
canvas.getByRole('button', {name: 'Clear Meeting time'}),
);
await userEvent.click(canvas.getByRole('button', {name: 'Open calendar'}));

await waitFor(() => {
expect(
canvas.queryAllByRole('gridcell', {selected: true, hidden: true}),
).toHaveLength(0);
});
},
};

export const WithMinMax: Story = {
render: args => {
const [value, setValue] = useState<ISODateTimeString | undefined>(
Expand Down
18 changes: 18 additions & 0 deletions packages/core/src/DateTimeInput/DateTimeInput.doc.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,12 @@ export const docs = {
description:
'Text above the input describing what datetime is expected.',
},
{
name: 'Segment row',
required: true,
description:
'The unbordered layout row that holds the two bordered segments and wraps them when its container is narrow.',
},
{
name: 'Date input',
required: true,
Expand All @@ -312,12 +318,24 @@ export const docs = {
description:
'A real input type=time for the default minute-precision native mode, a text/combobox time field when seconds, custom increments, or preset options are requested, or a read-only segment opening accessible time wheels under presentation="adaptive-bottom-sheet" on a coarse pointer.',
},
{
name: 'Clock icon',
required: true,
description:
'The time glyph shown as a leading identifier on the Astryx pointer field and as the picker affordance in native and touch-sheet time segments.',
},
{
name: 'Time options popover',
required: false,
description:
"A list of preset times at the timeOptionInterval cadence. Setting the prop retains Astryx's text/combobox time field even when nativePicker otherwise selects native controls; the Astryx touch sheet uses wheels instead.",
},
{
name: 'Time option',
required: false,
description:
'One selectable preset time in the optional time-options popover.',
},
{
name: 'Clear button',
required: false,
Expand Down
Loading
Loading