diff --git a/README.md b/README.md index 3a23de0b..e1a13646 100644 --- a/README.md +++ b/README.md @@ -96,6 +96,7 @@ Displays an input field complete with custom inputs, native input, and a calenda | dayPlaceholder | `placeholder` for the day input. | `"--"` | `"dd"` | | disabled | Whether the date picker should be disabled. | `false` | `true` | | disableCalendar | When set to `true`, will remove the calendar and the button toggling its visibility. | `false` | `true` | +| firstInputRef | A ref that will be a passed to the first input. | n/a | const firstInput = useRef(null); | | format | Input format based on [Unicode Technical Standard #35](https://www.unicode.org/reports/tr35/tr35-dates.html#Date_Field_Symbol_Table). Supported values are: `y`, `M`, `MM`, `MMM`, `MMMM`, `d`, `dd`. | n/a | `"y-MM-dd"` | | isOpen | Whether the calendar should be opened. | `false` | `true` | | locale | Locale that should be used by the date picker and the calendar. Can be any [IETF language tag](https://en.wikipedia.org/wiki/IETF_language_tag). | User's browser settings | `"hu-HU"` | diff --git a/src/DateInput.tsx b/src/DateInput.tsx index dec82cee..b24189c8 100644 --- a/src/DateInput.tsx +++ b/src/DateInput.tsx @@ -11,7 +11,7 @@ import NativeInput from './DateInput/NativeInput'; import { getFormatter } from './shared/dateFormatter'; import { getBegin, getEnd } from './shared/dates'; -import { isMaxDate, isMinDate } from './shared/propTypes'; +import { isMaxDate, isMinDate, isRef } from './shared/propTypes'; import { between } from './shared/utils'; import type { Detail, LooseValuePiece, Value } from './shared/types'; @@ -179,6 +179,7 @@ type DateInputProps = { className: string; dayAriaLabel?: string; dayPlaceholder?: string; + firstInputRef?: React.MutableRefObject; disabled?: boolean; format?: string; isCalendarOpen?: boolean | null; @@ -206,6 +207,7 @@ export default function DateInput({ dayAriaLabel, dayPlaceholder, disabled, + firstInputRef, format, isCalendarOpen: isCalendarOpenProps = null, locale, @@ -573,6 +575,7 @@ export default function DateInput({ // eslint-disable-next-line jsx-a11y/no-autofocus autoFocus={index === 0 && autoFocus} inputRef={dayInput} + {...(index === 0 && firstInputRef && { firstInputRef })} month={month} placeholder={dayPlaceholder} showLeadingZeros={showLeadingZerosFromFormat || showLeadingZeros} @@ -596,6 +599,7 @@ export default function DateInput({ // eslint-disable-next-line jsx-a11y/no-autofocus autoFocus={index === 0 && autoFocus} inputRef={monthSelect} + {...(index === 0 && firstInputRef && { firstInputRef })} locale={locale} placeholder={monthPlaceholder} short={currentMatch.length === 3} @@ -615,6 +619,7 @@ export default function DateInput({ // eslint-disable-next-line jsx-a11y/no-autofocus autoFocus={index === 0 && autoFocus} inputRef={monthInput} + {...(index === 0 && firstInputRef && { firstInputRef })} placeholder={monthPlaceholder} showLeadingZeros={showLeadingZerosFromFormat || showLeadingZeros} value={month} @@ -632,6 +637,7 @@ export default function DateInput({ // eslint-disable-next-line jsx-a11y/no-autofocus autoFocus={index === 0 && autoFocus} inputRef={yearInput} + {...(index === 0 && firstInputRef && { firstInputRef })} placeholder={yearPlaceholder} value={year} valueType={valueType} @@ -684,6 +690,7 @@ DateInput.propTypes = { dayAriaLabel: PropTypes.string, dayPlaceholder: PropTypes.string, disabled: PropTypes.bool, + firstInputRef: isRef, format: PropTypes.string, isCalendarOpen: PropTypes.bool, locale: PropTypes.string, diff --git a/src/DateInput/DayInput.spec.tsx b/src/DateInput/DayInput.spec.tsx index 48794051..0927d427 100644 --- a/src/DateInput/DayInput.spec.tsx +++ b/src/DateInput/DayInput.spec.tsx @@ -137,6 +137,14 @@ describe('DayInput', () => { expect(input).toBeRequired(); }); + it('handles firstInputRef properly', () => { + const firstInputRef = createRef(); + + render(); + + expect(firstInputRef.current).toBeInstanceOf(HTMLInputElement); + }); + it('handles inputRef properly', () => { const inputRef = createRef(); diff --git a/src/DateInput/Input.tsx b/src/DateInput/Input.tsx index d6e56fd7..0e0edd0e 100644 --- a/src/DateInput/Input.tsx +++ b/src/DateInput/Input.tsx @@ -12,7 +12,8 @@ type InputProps = { autoFocus?: boolean; className?: string; disabled?: boolean; - inputRef?: React.RefObject; + firstInputRef?: React.MutableRefObject; + inputRef?: React.MutableRefObject; max: number; min: number; name: string; @@ -138,6 +139,7 @@ export default function Input({ autoFocus, className, disabled, + firstInputRef, inputRef, max, min, @@ -199,7 +201,14 @@ export default function Input({ } }} placeholder={placeholder} - ref={inputRef} + ref={(el) => { + if (inputRef) { + inputRef.current = el; + } + if (firstInputRef) { + firstInputRef.current = el; + } + }} required={required} step={step} type="number" @@ -214,6 +223,7 @@ Input.propTypes = { autoFocus: PropTypes.bool, className: PropTypes.string.isRequired, disabled: PropTypes.bool, + firstInputRef: isRef, inputRef: isRef, max: PropTypes.number, min: PropTypes.number, diff --git a/src/DateInput/MonthInput.spec.tsx b/src/DateInput/MonthInput.spec.tsx index 201711c1..b60cb190 100644 --- a/src/DateInput/MonthInput.spec.tsx +++ b/src/DateInput/MonthInput.spec.tsx @@ -137,6 +137,14 @@ describe('MonthInput', () => { expect(input).toBeRequired(); }); + it('handles firstInputRef properly', () => { + const firstInputRef = createRef(); + + render(); + + expect(firstInputRef.current).toBeInstanceOf(HTMLInputElement); + }); + it('handles inputRef properly', () => { const inputRef = createRef(); diff --git a/src/DateInput/MonthSelect.spec.tsx b/src/DateInput/MonthSelect.spec.tsx index 788b276c..934739b4 100644 --- a/src/DateInput/MonthSelect.spec.tsx +++ b/src/DateInput/MonthSelect.spec.tsx @@ -113,6 +113,14 @@ describe('MonthSelect', () => { expect(select).toBeRequired(); }); + it('handles firstInputRef properly', () => { + const firstInputRef = createRef(); + + render(); + + expect(firstInputRef.current).toBeInstanceOf(HTMLSelectElement); + }); + it('handles inputRef properly', () => { const inputRef = createRef(); diff --git a/src/DateInput/MonthSelect.tsx b/src/DateInput/MonthSelect.tsx index dc2a7a16..428eb899 100644 --- a/src/DateInput/MonthSelect.tsx +++ b/src/DateInput/MonthSelect.tsx @@ -14,7 +14,8 @@ type MonthSelectProps = { autoFocus?: boolean; className: string; disabled?: boolean; - inputRef?: React.RefObject; + firstInputRef?: React.MutableRefObject; + inputRef?: React.MutableRefObject; locale?: string; maxDate?: Date; minDate?: Date; @@ -34,6 +35,7 @@ export default function MonthSelect({ autoFocus, className, disabled, + firstInputRef, inputRef, locale, maxDate, @@ -67,7 +69,14 @@ export default function MonthSelect({ name={name} onChange={onChange} onKeyDown={onKeyDown} - ref={inputRef} + ref={(el) => { + if (inputRef) { + inputRef.current = el; + } + if (firstInputRef) { + firstInputRef.current = el; + } + }} required={required} value={value !== null ? value : ''} > @@ -91,6 +100,7 @@ MonthSelect.propTypes = { autoFocus: PropTypes.bool, className: PropTypes.string.isRequired, disabled: PropTypes.bool, + firstInputRef: isRef, inputRef: isRef, locale: PropTypes.string, maxDate: isMaxDate, diff --git a/src/DateInput/YearInput.spec.tsx b/src/DateInput/YearInput.spec.tsx index fd6bd446..e71630b5 100644 --- a/src/DateInput/YearInput.spec.tsx +++ b/src/DateInput/YearInput.spec.tsx @@ -101,6 +101,14 @@ describe('YearInput', () => { expect(input).toBeRequired(); }); + it('handles firstInputRef properly', () => { + const firstInputRef = createRef(); + + render(); + + expect(firstInputRef.current).toBeInstanceOf(HTMLInputElement); + }); + it('handles inputRef properly', () => { const inputRef = createRef(); diff --git a/src/DatePicker.tsx b/src/DatePicker.tsx index 9249e63a..ef00d784 100644 --- a/src/DatePicker.tsx +++ b/src/DatePicker.tsx @@ -8,7 +8,7 @@ import Fit from 'react-fit'; import DateInput from './DateInput'; -import { isMaxDate, isMinDate, rangeOf } from './shared/propTypes'; +import { isMaxDate, isMinDate, rangeOf, isRef } from './shared/propTypes'; import type { ClassName, CloseReason, Detail, LooseValue, OpenReason, Value } from './shared/types'; @@ -71,6 +71,7 @@ export type DatePickerProps = { dayPlaceholder?: string; disableCalendar?: boolean; disabled?: boolean; + firstInputRef?: React.MutableRefObject; format?: string; id?: string; isOpen?: boolean; @@ -114,6 +115,7 @@ export default function DatePicker(props: DatePickerProps) { dayPlaceholder, disableCalendar, disabled, + firstInputRef, format, id, isOpen: isOpenProps = null, @@ -308,6 +310,7 @@ export default function DatePicker(props: DatePickerProps) { autoFocus={autoFocus} className={`${baseClassName}__inputGroup`} disabled={disabled} + firstInputRef={firstInputRef} format={format} isCalendarOpen={isOpen} locale={locale} @@ -439,6 +442,7 @@ DatePicker.propTypes = { dayPlaceholder: PropTypes.string, disableCalendar: PropTypes.bool, disabled: PropTypes.bool, + firstInputRef: isRef, format: PropTypes.string, id: PropTypes.string, isOpen: PropTypes.bool, diff --git a/test/Test.css b/test/Test.css index fbc0b4f0..4c01564e 100644 --- a/test/Test.css +++ b/test/Test.css @@ -90,3 +90,12 @@ body { flex-grow: 100; align-items: stretch; } + +.Test__container__content__label { + background: none !important; + border: none; + padding: 0 !important; + display: block; + font-size: 1rem; + font-weight: bold; +} diff --git a/test/Test.tsx b/test/Test.tsx index 136e1347..914eec47 100644 --- a/test/Test.tsx +++ b/test/Test.tsx @@ -1,4 +1,4 @@ -import React, { useRef, useState } from 'react'; +import React, { useRef, useState, type MouseEvent } from 'react'; import DatePicker from 'react-date-picker/src'; import 'react-date-picker/src/DatePicker.css'; import 'react-calendar/dist/Calendar.css'; @@ -53,6 +53,14 @@ export default function Test() { const [showNeighboringMonth, setShowNeighboringMonth] = useState(false); const [showWeekNumbers, setShowWeekNumbers] = useState(false); const [value, setValue] = useState(now); + const firstInputRef = useRef(null); + + const focusDatePicker = (e: MouseEvent) => { + e.preventDefault(); + if (firstInputRef.current instanceof HTMLInputElement) { + firstInputRef.current.focus(); + } + }; return (
@@ -103,6 +111,13 @@ export default function Test() { console.log(event); }} > +