CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-react-dates

A responsive and accessible date range picker component built with React

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

input-components.mddocs/

Input Components

Standalone input field components for custom layouts and advanced use cases. These components handle date input, validation, and formatting without the calendar interface, allowing you to build custom date picker experiences.

Capabilities

DateRangePickerInput

Input fields component for date range display and manual entry, providing dual input fields for start and end dates.

/**
 * Date range input fields without calendar
 * @param props - DateRangePickerInput configuration
 * @returns Input fields for date range entry
 */
function DateRangePickerInput(props: DateRangePickerInputProps): ReactElement;

interface DateRangePickerInputProps {
  // Input field IDs (required)
  startDateId: string;
  endDateId: string;

  // Display values
  startDate?: string;
  endDate?: string;
  startDatePlaceholderText?: string;
  endDatePlaceholderText?: string;

  // State
  isStartDateFocused?: boolean;
  isEndDateFocused?: boolean;
  isFocused?: boolean;                              // Actual DOM focus state
  disabled?: boolean;
  required?: boolean;
  readOnly?: boolean;
  showClearDates?: boolean;
  showCaret?: boolean;                              // Show caret indicator

  // Styling
  noBorder?: boolean;
  block?: boolean;
  small?: boolean;
  regular?: boolean;
  isRTL?: boolean;                                  // Right-to-left layout support

  // Layout
  openDirection?: 'up' | 'down';                   // Direction to open calendar overlay
  verticalSpacing?: number;                        // Vertical spacing in pixels (non-negative integer)

  // Icons
  showDefaultInputIcon?: boolean;
  inputIconPosition?: 'before' | 'after';
  customInputIcon?: ReactNode;
  customArrowIcon?: ReactNode;
  customCloseIcon?: ReactNode;

  // Event handlers
  onStartDateChange?: (dateString: string) => void;
  onEndDateChange?: (dateString: string) => void;
  onStartDateFocus?: () => void;
  onEndDateFocus?: () => void;
  onStartDateShiftTab?: () => void;
  onEndDateTab?: () => void;
  onClearDates?: () => void;
  onKeyDownArrowDown?: () => void;                 // Arrow down key handler
  onKeyDownQuestionMark?: () => void;              // Question mark key handler

  // Accessibility
  startDateAriaLabel?: string;
  endDateAriaLabel?: string;
  screenReaderMessage?: string;

  // Content
  children?: ReactNode;                            // Child elements to render

  // Internationalization
  phrases?: object;
}

Usage Examples:

import React, { useState } from "react";
import { DateRangePickerInput } from "react-dates";

// Basic date range input
function BasicDateRangeInput() {
  const [startDate, setStartDate] = useState('');
  const [endDate, setEndDate] = useState('');
  const [startFocused, setStartFocused] = useState(false);
  const [endFocused, setEndFocused] = useState(false);

  return (
    <DateRangePickerInput
      startDateId="start_date"
      endDateId="end_date"
      startDate={startDate}
      endDate={endDate}
      onStartDateChange={setStartDate}
      onEndDateChange={setEndDate}
      isStartDateFocused={startFocused}
      isEndDateFocused={endFocused}
      onStartDateFocus={() => setStartFocused(true)}
      onEndDateFocus={() => setEndFocused(true)}
      startDatePlaceholderText="Check-in"
      endDatePlaceholderText="Check-out"
      showClearDates={true}
    />
  );
}

// Custom styled input with validation
function CustomDateRangeInput({ onValidDatesChange }) {
  const [startDate, setStartDate] = useState('');
  const [endDate, setEndDate] = useState('');
  const [startFocused, setStartFocused] = useState(false);
  const [endFocused, setEndFocused] = useState(false);
  const [errors, setErrors] = useState({});

  const validateDate = (dateString) => {
    const date = moment(dateString, 'MM/DD/YYYY', true);
    return date.isValid() && date.isAfter(moment().subtract(1, 'day'));
  };

  const handleStartDateChange = (dateString) => {
    setStartDate(dateString);
    
    const isValid = validateDate(dateString);
    setErrors(prev => ({ ...prev, start: !isValid }));
    
    if (isValid && validateDate(endDate)) {
      onValidDatesChange({
        startDate: moment(dateString, 'MM/DD/YYYY'),
        endDate: moment(endDate, 'MM/DD/YYYY')
      });
    }
  };

  const handleEndDateChange = (dateString) => {
    setEndDate(dateString);
    
    const isValid = validateDate(dateString);
    setErrors(prev => ({ ...prev, end: !isValid }));
    
    if (isValid && validateDate(startDate)) {
      onValidDatesChange({
        startDate: moment(startDate, 'MM/DD/YYYY'),
        endDate: moment(dateString, 'MM/DD/YYYY')
      });
    }
  };

  return (
    <div>
      <DateRangePickerInput
        startDateId="trip_start"
        endDateId="trip_end"
        startDate={startDate}
        endDate={endDate}
        onStartDateChange={handleStartDateChange}
        onEndDateChange={handleEndDateChange}
        isStartDateFocused={startFocused}
        isEndDateFocused={endFocused}
        onStartDateFocus={() => setStartFocused(true)}
        onEndDateFocus={() => setEndFocused(true)}
        showDefaultInputIcon={true}
        block={true}
      />
      {errors.start && <div style={{ color: 'red' }}>Invalid start date</div>}
      {errors.end && <div style={{ color: 'red' }}>Invalid end date</div>}
    </div>
  );
}

DateRangePickerInputController

Controller component that manages date range input logic, validation, and formatting.

/**
 * Controller for date range input logic and validation
 * @param props - DateRangePickerInputController configuration
 * @returns Managed date range input component
 */
function DateRangePickerInputController(props: DateRangePickerInputControllerProps): ReactElement;

interface DateRangePickerInputControllerProps {
  // Date state
  startDate?: moment.Moment | null;
  endDate?: moment.Moment | null;

  // Input field IDs (required)
  startDateId: string;
  endDateId: string;

  // Display format and placeholders
  startDatePlaceholderText?: string;
  endDatePlaceholderText?: string;
  displayFormat?: (() => string) | string;
  
  // State management
  isStartDateFocused?: boolean;
  isEndDateFocused?: boolean;
  isFocused?: boolean;                              // Actual DOM focus state
  disabled?: boolean;
  required?: boolean;
  readOnly?: boolean;
  showClearDates?: boolean;
  showCaret?: boolean;                              // Show caret indicator
  keepOpenOnDateSelect?: boolean;                  // Keep picker open after date selection
  reopenPickerOnClearDates?: boolean;              // Reopen picker when dates are cleared

  // Portal and layout
  withFullScreenPortal?: boolean;                  // Use full screen portal mode
  openDirection?: 'up' | 'down';                   // Direction to open calendar
  verticalSpacing?: number;                        // Vertical spacing in pixels (non-negative integer)
  isRTL?: boolean;                                  // Right-to-left layout support

  // Date validation
  minimumNights?: number;                          // Minimum nights between dates (non-negative integer)
  isOutsideRange?: (day: moment.Moment) => boolean; // Function to determine if day is outside selectable range

  // Event handlers
  onDatesChange?: ({ startDate, endDate }: {
    startDate: moment.Moment | null;
    endDate: moment.Moment | null;
  }) => void;
  onFocusChange?: (focusedInput: 'startDate' | 'endDate' | null) => void;
  onClose?: () => void;                            // Callback when picker closes
  onKeyDownArrowDown?: () => void;                 // Arrow down key handler
  onKeyDownQuestionMark?: () => void;              // Question mark key handler

  // Styling
  noBorder?: boolean;
  block?: boolean;
  small?: boolean;
  regular?: boolean;

  // Icons
  showDefaultInputIcon?: boolean;
  inputIconPosition?: 'before' | 'after';
  customInputIcon?: ReactNode;
  customArrowIcon?: ReactNode;
  customCloseIcon?: ReactNode;

  // Accessibility
  startDateAriaLabel?: string;
  endDateAriaLabel?: string;
  screenReaderMessage?: string;

  // Content
  children?: ReactNode;                            // Child elements to render

  // Internationalization
  phrases?: object;
}

Public Methods:

// Date handling methods (accessible via ref)
onStartDateChange(startDateString: string): void;
onEndDateChange(endDateString: string): void;
onStartDateFocus(): void;
onEndDateFocus(): void;
clearDates(): void;
getDisplayFormat(): string;
getDateString(date: moment.Moment | null): string;

Usage Examples:

import React, { useState, useRef } from "react";
import { DateRangePickerInputController } from "react-dates";
import moment from "moment";

// Controlled date range input
function ControlledDateRangeInput() {
  const [startDate, setStartDate] = useState(null);
  const [endDate, setEndDate] = useState(null);
  const [focusedInput, setFocusedInput] = useState(null);
  const controllerRef = useRef(null);

  const handleDatesChange = ({ startDate, endDate }) => {
    setStartDate(startDate);
    setEndDate(endDate);
    
    // Custom validation
    if (startDate && endDate && endDate.isBefore(startDate)) {
      // Swap dates if end is before start
      setStartDate(endDate);
      setEndDate(startDate);
    }
  };

  const clearAllDates = () => {
    if (controllerRef.current) {
      controllerRef.current.clearDates();
    }
  };

  return (
    <div>
      <DateRangePickerInputController
        ref={controllerRef}
        startDate={startDate}
        endDate={endDate}
        startDateId="start_input"
        endDateId="end_input"
        onDatesChange={handleDatesChange}
        onFocusChange={setFocusedInput}
        isStartDateFocused={focusedInput === 'startDate'}
        isEndDateFocused={focusedInput === 'endDate'}
        displayFormat="MMM D, YYYY"
        showClearDates={true}
      />
      <button onClick={clearAllDates}>Clear All Dates</button>
    </div>
  );
}

SingleDatePickerInput

Input field component for single date display and manual entry.

/**
 * Single date input field without calendar
 * @param props - SingleDatePickerInput configuration
 * @returns Input field for single date entry
 */
function SingleDatePickerInput(props: SingleDatePickerInputProps): ReactElement;

interface SingleDatePickerInputProps {
  // Required
  id: string;

  // Display
  placeholder?: string;
  displayValue?: string;
  ariaLabel?: string;
  screenReaderMessage?: string;

  // State
  focused?: boolean;
  disabled?: boolean;
  required?: boolean;
  readOnly?: boolean;
  showClearDate?: boolean;

  // Styling
  noBorder?: boolean;
  block?: boolean;
  small?: boolean;
  regular?: boolean;

  // Icons
  showDefaultInputIcon?: boolean;
  inputIconPosition?: 'before' | 'after';
  customInputIcon?: ReactNode;
  customCloseIcon?: ReactNode;

  // Event handlers
  onChange?: (dateString: string) => void;
  onClearDate?: () => void;
  onFocus?: () => void;
  onKeyDownShiftTab?: () => void;
  onKeyDownTab?: () => void;
  onKeyDownArrowDown?: () => void;
  onKeyDownQuestionMark?: () => void;

  // Internationalization
  phrases?: object;
}

Usage Examples:

import React, { useState } from "react";
import { SingleDatePickerInput } from "react-dates";
import moment from "moment";

// Basic single date input
function BasicSingleDateInput() {
  const [date, setDate] = useState('');
  const [focused, setFocused] = useState(false);

  const handleDateChange = (dateString) => {
    setDate(dateString);
    
    // Validate date format
    const parsedDate = moment(dateString, 'MM/DD/YYYY', true);
    if (parsedDate.isValid()) {
      console.log('Valid date selected:', parsedDate.format('YYYY-MM-DD'));
    }
  };

  return (
    <SingleDatePickerInput
      id="birthday_input"
      placeholder="Select your birthday"
      displayValue={date}
      onChange={handleDateChange}
      focused={focused}
      onFocus={() => setFocused(true)}
      showClearDate={true}
      showDefaultInputIcon={true}
    />
  );
}

// Formatted date input with custom validation
function FormattedDateInput({ format = 'YYYY-MM-DD', onValidDate }) {
  const [inputValue, setInputValue] = useState('');
  const [isValid, setIsValid] = useState(true);

  const handleChange = (dateString) => {
    setInputValue(dateString);
    
    const parsedDate = moment(dateString, format, true);
    const valid = parsedDate.isValid();
    setIsValid(valid);
    
    if (valid) {
      onValidDate(parsedDate);
    }
  };

  return (
    <div>
      <SingleDatePickerInput
        id="formatted_date"
        placeholder={`Enter date (${format})`}
        displayValue={inputValue}
        onChange={handleChange}
        block={true}
        customInputIcon={
          <span style={{ color: isValid ? 'green' : 'red' }}>📅</span>
        }
      />
      {!isValid && inputValue && (
        <div style={{ color: 'red', fontSize: '12px' }}>
          Please enter a valid date in {format} format
        </div>
      )}
    </div>
  );
}

SingleDatePickerInputController

Controller component that manages single date input logic, validation, and formatting with full picker integration.

/**
 * Controller for single date input logic with picker integration
 * @param props - SingleDatePickerInputController configuration
 * @returns Managed single date input component with picker
 */
function SingleDatePickerInputController(props: SingleDatePickerInputControllerProps): ReactElement;

interface SingleDatePickerInputControllerProps {
  // Date state
  date?: moment.Moment | null;                     // Selected date value
  focused?: boolean;                               // Whether input is focused

  // Required props
  id: string;                                      // Required input field ID
  onDateChange: (date: moment.Moment | null) => void; // Required callback when date changes
  onFocusChange: (arg: { focused: boolean }) => void; // Required callback for focus changes

  // Display and placeholders
  placeholder?: string;                            // Input placeholder text
  displayFormat?: string | (() => string);        // Date display format
  
  // State management
  isFocused?: boolean;                             // Actual DOM focus state
  disabled?: boolean;                              // Disabled state
  required?: boolean;                              // Required field
  readOnly?: boolean;                              // Read-only state
  showClearDate?: boolean;                         // Show clear date button
  showCaret?: boolean;                             // Show caret indicator

  // Portal and layout
  openDirection?: 'up' | 'down';                  // Direction to open calendar
  verticalSpacing?: number;                       // Vertical spacing in pixels (non-negative integer)
  isRTL?: boolean;                                 // Right-to-left layout support

  // Picker behavior
  keepOpenOnDateSelect?: boolean;                  // Keep open after date selection
  reopenPickerOnClearDate?: boolean;              // Reopen after clearing date

  // Date validation
  isOutsideRange?: (day: moment.Moment) => boolean; // Outside range validation

  // Event handlers
  onClose?: () => void;                            // Callback when picker closes
  onKeyDownArrowDown?: () => void;                 // Arrow down key handler
  onKeyDownQuestionMark?: () => void;              // Question mark key handler

  // Styling
  noBorder?: boolean;                              // Remove border styling
  block?: boolean;                                 // Block-level display
  small?: boolean;                                 // Small size variant
  regular?: boolean;                               // Regular size variant

  // Icons
  showDefaultInputIcon?: boolean;                  // Show default calendar icon
  inputIconPosition?: 'before' | 'after';         // Position of input icon
  customInputIcon?: ReactNode;                     // Custom input icon
  customCloseIcon?: ReactNode;                     // Custom close icon

  // Accessibility
  ariaLabel?: string;                              // Accessibility label
  screenReaderMessage?: string;                    // Screen reader message

  // Content
  children?: ReactNode;                            // Child elements to render

  // Internationalization
  phrases?: object;                                // Internationalization phrases
}

Usage Examples:

import React, { useState } from "react";
import { SingleDatePickerInputController } from "react-dates";
import moment from "moment";

// Controlled single date input with picker
function ControlledSingleDateInput() {
  const [date, setDate] = useState(null);
  const [focused, setFocused] = useState(false);

  return (
    <SingleDatePickerInputController
      id="controlled_date"
      date={date}
      onDateChange={setDate}
      focused={focused}
      onFocusChange={({ focused }) => setFocused(focused)}
      placeholder="Select a date"
      showClearDate={true}
      showDefaultInputIcon={true}
      isOutsideRange={(day) => moment().diff(day) > 0} // No past dates
    />
  );
}

// Advanced date picker with validation
function ValidatedDatePicker({ onValidDate, minDate, maxDate }) {
  const [date, setDate] = useState(null);
  const [focused, setFocused] = useState(false);

  const isOutsideRange = (day) => {
    if (minDate && day.isBefore(minDate, 'day')) return true;
    if (maxDate && day.isAfter(maxDate, 'day')) return true;
    return false;
  };

  const handleDateChange = (newDate) => {
    setDate(newDate);
    if (newDate && !isOutsideRange(newDate)) {
      onValidDate(newDate);
    }
  };

  return (
    <SingleDatePickerInputController
      id="validated_date"
      date={date}
      onDateChange={handleDateChange}
      focused={focused}
      onFocusChange={({ focused }) => setFocused(focused)}
      isOutsideRange={isOutsideRange}
      displayFormat="MMM D, YYYY"
      block={true}
      onClose={() => console.log('Picker closed')}
    />
  );
}

Common Input Features

Icon Customization

All input components support custom icons:

// Custom calendar icon
const customIcon = <CalendarIcon size={16} color="#007bff" />;

// Custom clear button
const customClearIcon = <XIcon size={14} color="#dc3545" />;

<SingleDatePickerInput
  customInputIcon={customIcon}
  customCloseIcon={customClearIcon}
  inputIconPosition="before"
/>

Accessibility Features

  • ARIA labeling for screen readers
  • Keyboard navigation support
  • Focus management
  • Screen reader announcements
  • Customizable accessible text

Validation Patterns

// Date format validation
const validateDateFormat = (dateString, format = 'MM/DD/YYYY') => {
  const date = moment(dateString, format, true);
  return date.isValid();
};

// Date range validation
const validateDateRange = (date, minDate, maxDate) => {
  const momentDate = moment(date);
  return momentDate.isBetween(minDate, maxDate, 'day', '[]');
};

// Business day validation
const isBusinessDay = (dateString) => {
  const date = moment(dateString);
  const dayOfWeek = date.day();
  return dayOfWeek !== 0 && dayOfWeek !== 6; // Not Sunday or Saturday
};

Install with Tessl CLI

npx tessl i tessl/npm-react-dates

docs

calendar-components.md

calendar-controllers.md

index.md

input-components.md

main-pickers.md

utilities.md

tile.json