Skip to content
Open
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
37 changes: 36 additions & 1 deletion packages/react/src/components/DatePicker/DatePicker-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* LICENSE file in the root directory of this source tree.
*/

import React, { useState } from 'react';
import React, { createRef, useState } from 'react';
import DatePicker from './DatePicker';
import DatePickerInput from '../DatePickerInput';
import { render, screen, fireEvent } from '@testing-library/react';
Expand Down Expand Up @@ -1250,6 +1250,41 @@ describe('Range date picker', () => {
expect(end.value).toBe('02/14/2025');
});

it('should not clear an input when setDate receives timestamp 0', async () => {
const ref = createRef();

render(
<DatePicker ref={ref} datePickerType="range" value={undefined}>
<DatePickerInput
id="start-zero"
placeholder="mm/dd/yyyy"
labelText="Start date"
data-testid="start-input-zero"
/>
<DatePickerInput
id="end-zero"
placeholder="mm/dd/yyyy"
labelText="End date"
data-testid="end-input-zero"
/>
</DatePicker>
);

const fp = ref.current.calendar;
const start = await screen.findByTestId('start-input-zero');
const end = await screen.findByTestId('end-input-zero');
const formattedZero = fp.formatDate(new Date(0), 'm/d/Y');
const formattedOne = fp.formatDate(new Date(1), 'm/d/Y');

fp.setDate([0, 1], false, 'm/d/Y');
expect(start.value).toBe(formattedZero);
expect(end.value).toBe(formattedOne);

fp.setDate([1, 0], false, 'm/d/Y');
expect(start.value).toBe(formattedOne);
expect(end.value).toBe(formattedZero);
});

it('should not write both dates into the first input', async () => {
const ref = React.createRef();

Expand Down
26 changes: 18 additions & 8 deletions packages/react/src/components/DatePicker/DatePicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import {
import type { Instance } from 'flatpickr/dist/types/instance';
import { datePartsOrder } from '@carbon/utilities';
import { SUPPORTED_LOCALES, type SupportedLocale } from './DatePickerLocales';
import { isEmptyDateValue } from './utils';

// Weekdays shorthand for English locale
// Ensure localization exists before trying to access it
Expand Down Expand Up @@ -206,8 +207,10 @@ export type DatePickerTypes = 'simple' | 'single' | 'range';

export interface DatePickerProps {
/**
* Flatpickr prop passthrough enables direct date input, and when set to false,
* we must clear dates manually by resetting the value prop to to a falsy value (such as `""`, `null`, or `undefined`) or an array of all falsy values, making it a controlled input.
* Flatpickr prop passthrough enables direct date input, and when set to
* false, we must clear dates manually by resetting the value prop to an empty
* value (such as `""`, `null`, or `undefined`) or an array of all empty
* values, making it a controlled input.
*/
allowInput?: boolean;

Expand Down Expand Up @@ -912,10 +915,11 @@ const DatePicker = forwardRef<HTMLDivElement, DatePickerProps>((props, ref) => {
useEffect(() => {
// when value prop is manually reset, this clears the flatpickr calendar instance and text input
// run if both:
// 1. value prop is set to a falsy value (`""`, `undefined`, `null`, etc) OR an array of all falsy values
// 1. value prop is set to an empty value (`""`, `undefined`, `null`, etc) OR an array of all empty values
// 2. flatpickr instance contains values in its `selectedDates` property so it hasn't already been cleared
if (
(!value || (Array.isArray(value) && value.every((date) => !date))) &&
(isEmptyDateValue(value) ||
(Array.isArray(value) && value.every(isEmptyDateValue))) &&
calendarRef.current?.selectedDates.length
) {
calendarRef.current?.clear();
Expand All @@ -938,7 +942,7 @@ const DatePicker = forwardRef<HTMLDivElement, DatePickerProps>((props, ref) => {
value === '' ||
value === null ||
(Array.isArray(value) &&
(value.length === 0 || value.every((element) => !element)))
(value.length === 0 || value.every(isEmptyDateValue)))
) {
// only clear if there are selected dates to avoid unnecessary operations
if (calendarRef.current.selectedDates.length > 0) {
Expand All @@ -950,7 +954,11 @@ const DatePicker = forwardRef<HTMLDivElement, DatePickerProps>((props, ref) => {
}
updateClassNames(calendarRef.current, prefix);
//for simple date picker w/o calendar; initial mount may not have value
} else if (!calendarRef.current && value) {
} else if (
!calendarRef.current &&
typeof value !== 'undefined' &&
value !== null
) {
startInputField.current.value = value;
}
}, [value, prefix, startInputField]);
Expand Down Expand Up @@ -992,8 +1000,10 @@ const DatePicker = forwardRef<HTMLDivElement, DatePickerProps>((props, ref) => {

DatePicker.propTypes = {
/**
* Flatpickr prop passthrough enables direct date input, and when set to false,
* we must clear dates manually by resetting the value prop to a falsy value (such as `""`, `null`, or `undefined`) or an array of all falsy values, making it a controlled input.
* Flatpickr prop passthrough enables direct date input, and when set to
* false, we must clear dates manually by resetting the value prop to an empty
* value (such as `""`, `null`, or `undefined`) or an array of all empty
* values, making it a controlled input.
*/
allowInput: PropTypes.bool,

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Copyright IBM Corp. 2019, 2025
* Copyright IBM Corp. 2019, 2026
*
* This source code is licensed under the Apache-2.0 license found in the
* LICENSE file in the root directory of this source tree.
Expand All @@ -9,6 +9,7 @@ import baseRangePlugin, {
type Config,
} from 'flatpickr/dist/plugins/rangePlugin';
import { Instance } from 'flatpickr/dist/types/instance';
import { isEmptyDateValue } from '../utils';

/**
* @param config Plugin configuration.
Expand Down Expand Up @@ -37,7 +38,7 @@ export const rangePlugin = (config: Config = {}) => {

[inputFrom, inputToElement].forEach((input, i) => {
if (input && input instanceof HTMLInputElement) {
input.value = !dates[i]
input.value = isEmptyDateValue(dates[i])
? ''
: formatDate(new Date(dates[i]), fp.config.dateFormat);
}
Expand Down
9 changes: 9 additions & 0 deletions packages/react/src/components/DatePicker/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/**
* Copyright IBM Corp. 2026
*
* This source code is licensed under the Apache-2.0 license found in the
* LICENSE file in the root directory of this source tree.
*/

export const isEmptyDateValue = (date: unknown) =>
date === '' || date === null || typeof date === 'undefined';
Loading