Skip to content

Commit

Permalink
fix: datepicker blocks input field
Browse files Browse the repository at this point in the history
  • Loading branch information
MarcoKorinth committed Aug 31, 2023
1 parent 15dbe56 commit 553bd28
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 49 deletions.
17 changes: 17 additions & 0 deletions frontend/src/js/common/helpers/mergeRefs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
type Mutable<T> = {
-readonly [k in keyof T]: T[k];
};

export const mergeRefs =
<T>(...refs: React.Ref<T>[]) =>
(value: T): void => {
for (let i = 0; i < refs.length; i += 1) {
const ref = refs[i];

if (typeof ref === "function") {
ref(value);
} else if (ref) {
(ref as Mutable<React.RefObject<T>>).current = value;
}
}
};
2 changes: 1 addition & 1 deletion frontend/src/js/ui-components/InputDate/CustomHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ const YearMonthSelect = ({
value: i,
}));

const [yearSelectOpen, setYearSelectOpen] = useState(false);
const [yearSelectOpen, setYearSelectOpen] = useState(true);
const [monthSelectOpen, setMonthSelectOpen] = useState(false);
const handleClick = () => {
if (yearSelectOpen || monthSelectOpen) {
Expand Down
84 changes: 38 additions & 46 deletions frontend/src/js/ui-components/InputDate/InputDate.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import styled from "@emotion/styled";
import { createElement, forwardRef, useRef, useState } from "react";
import { createElement, forwardRef, useRef } from "react";
import ReactDatePicker from "react-datepicker";
import "react-datepicker/dist/react-datepicker.css";

import { formatDate, parseDate } from "../../common/helpers/dateHelper";
import BaseInput, { Props as BaseInputProps } from "../BaseInput";

import { CustomHeader } from "./CustomHeader";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faCalendar } from "@fortawesome/free-regular-svg-icons";
import { mergeRefs } from "../../common/helpers/mergeRefs";

const Root = styled("div")`
position: relative;
Expand All @@ -19,11 +22,25 @@ const Root = styled("div")`
}
.react-datepicker-popper[data-placement^="bottom"] {
padding-top: 4px;
transform: translate3d(0, 32px, 0) !important;
}
.react-datepicker-popper[data-placement^="top"] {
padding-bottom: 0;
transform: translate3d(0, 32px, 0) !important;
}
`;

const CalendarIcon = styled(FontAwesomeIcon)`
position: absolute;
width: 16px;
height: 16px;
top: calc(50% - 8px);
left: 5px;
cursor: pointer;
color: ${({ theme }) => theme.col.black};
`;

const StyledBaseInput = styled(BaseInput)`
input {
padding-left: 22px;
}
`;

Expand All @@ -45,28 +62,12 @@ type Props = Omit<BaseInputProps, "inputType"> & {
onCalendarSelect?: (val: string) => void;
};

// TODO: Remove this once we have solved
// - that the date picker overlays other fields in forms
const TEMPORARILY_DISABLED_DATE_PICKER = true;

const InputDate = forwardRef<HTMLInputElement, Props>(
const InputDate = forwardRef<ReactDatePicker, Props>(
(
{
className,
value,
dateFormat,
onChange,
onCalendarSelect,
onFocus,
onBlur,
onClick,
...props
},
{ className, value, dateFormat, onChange, onCalendarSelect, ...props },
ref,
) => {
const datePickerRef = useRef<ReactDatePicker>(null);
const [hasFocus, setHasFocus] = useState(false);
const [focusBlocked, setFocusBlocked] = useState(false);

return (
<Root
Expand All @@ -75,34 +76,13 @@ const InputDate = forwardRef<HTMLInputElement, Props>(
if (e.key === "Escape") datePickerRef.current?.setOpen(false);
}}
>
<BaseInput
<StyledBaseInput
{...props}
ref={ref}
inputType="text"
value={value}
onChange={(val) => {
onChange(val as string);
}}
onFocus={(e) => {
if (focusBlocked) {
e.currentTarget.blur();
setFocusBlocked(false);
} else {
onFocus?.(e);
setHasFocus(true);
datePickerRef.current?.setOpen(true);
}
}}
onBlur={(e) => {
onBlur?.(e);
setHasFocus(false);
}}
onClick={(e) => {
onClick?.(e);
if (hasFocus) {
datePickerRef.current?.setOpen(true);
}
}}
inputProps={{
...props?.inputProps,
onKeyPress: (e) => {
Expand All @@ -111,8 +91,12 @@ const InputDate = forwardRef<HTMLInputElement, Props>(
},
}}
/>
<CalendarIcon
icon={faCalendar}
onClick={() => datePickerRef.current?.setOpen(true)}
/>
<ReactDatePicker
ref={datePickerRef}
ref={mergeRefs(datePickerRef, ref)}
selected={value ? parseDate(value, dateFormat) : new Date()}
onChange={(val) => {
if (!val) {
Expand All @@ -122,15 +106,23 @@ const InputDate = forwardRef<HTMLInputElement, Props>(
const selectedDate = formatDate(val, dateFormat);
onChange(selectedDate);
onCalendarSelect?.(selectedDate);
setFocusBlocked(true);
datePickerRef.current?.setOpen(false);
}}
onClickOutside={() => datePickerRef.current?.setOpen(false)}
renderCustomHeader={CustomHeader}
customInput={createElement(HiddenInput)}
calendarContainer={StyledCalendar}
calendarStartDay={1}
disabled={TEMPORARILY_DISABLED_DATE_PICKER}
popperProps={{
modifiers: [
{
name: "preventOverflow",
options: {
mainAxis: false,
},
},
],
}}
/>
</Root>
);
Expand Down
5 changes: 3 additions & 2 deletions frontend/src/js/ui-components/InputDateRange.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { css } from "@emotion/react";
import styled from "@emotion/styled";
import { faCalendar } from "@fortawesome/free-regular-svg-icons";
import { FC, ReactNode, createRef, useMemo } from "react";
import ReactDatePicker from "react-datepicker";
import { useTranslation } from "react-i18next";

import { IndexPrefix } from "../common/components/IndexPrefix";
Expand Down Expand Up @@ -166,7 +167,7 @@ const InputDateRange: FC<PropsT> = ({
const min = getDisplayDate("min", value, displayDateFormat);
const max = getDisplayDate("max", value, displayDateFormat);

const maxRef = createRef<HTMLInputElement>();
const maxRef = createRef<ReactDatePicker>();

const isMinValid = exists(value.min && parseDate(min, displayDateFormat));
const isMaxValid = exists(value.max && parseDate(max, displayDateFormat));
Expand Down Expand Up @@ -213,7 +214,7 @@ const InputDateRange: FC<PropsT> = ({
onChange={(val) =>
onChangeRaw("min", val as string, displayDateFormat)
}
onCalendarSelect={() => maxRef.current?.focus()}
onCalendarSelect={() => maxRef.current?.setOpen(true)}
onBlur={(e) => applyDate("min", e.target.value, displayDateFormat)}
inputProps={{
autoFocus,
Expand Down

0 comments on commit 553bd28

Please sign in to comment.