Skip to content

Commit

Permalink
Add TimePicker component
Browse files Browse the repository at this point in the history
  • Loading branch information
jfdoming committed Oct 29, 2023
1 parent 73bd44b commit 7634bea
Show file tree
Hide file tree
Showing 10 changed files with 441 additions and 118 deletions.
2 changes: 1 addition & 1 deletion backend/services/implementations/testSessionService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -364,7 +364,7 @@ class TestSessionService implements ITestSessionService {
return (
!updatableKeys.has(key) &&
(currentValue instanceof Date
? currentValue.getTime() !== newValue.getTime()
? currentValue.getTime() !== new Date(newValue).getTime()
: currentValue?.toString() !== newValue)
);
},
Expand Down
145 changes: 79 additions & 66 deletions frontend/src/components/common/form/DatePicker.tsx
Original file line number Diff line number Diff line change
@@ -1,91 +1,104 @@
import type { ReactElement } from "react";
import React from "react";
import React, { useMemo } from "react";
import { SingleDatepicker } from "chakra-dayzed-datepicker";
import type { PropsConfigs } from "chakra-dayzed-datepicker/dist/utils/commonTypes";
import { format } from "date-fns";

const DATEPICKER_CONFIGS = {
dayNames: "SMTWTFS".split(""),
monthNames: [
"January",
"February",
"March",
"April",
"May",
"June",
"July",
"Auguest",
"September",
"October",
"November",
"December",
],
};

type DatePickerProps = {
name?: string;
onChange: (date: Date) => void;
value: Date | null | undefined;
isDisabled?: boolean;
};

const getDatePickerStyles = (
value?: Date | null,
isDisabled?: boolean,
): PropsConfigs => ({
dateNavBtnProps: {
fontWeight: 400,
},
dayOfMonthBtnProps: {
defaultBtnProps: {
width: "2rem",
color: "grey.400",
fontWeight: 400,
borderRadius: 20,
_hover: {
width: "2rem",
background: "blue.300",
color: "white",
bg: "blue.300",
},
},
selectedBtnProps: {
width: "2rem",
background: "blue.300",
borderRadius: 20,
color: "white",
},
todayBtnProps: {
width: "2rem",
borderColor: "grey.300",
borderRadius: 20,
borderWidth: "1px",
borderStyle: "solid",
},
},
inputProps: {
isDisabled,
type: "button",
cursor: "pointer",
"aria-label": "This is a date input, activate to open date picker",
textAlign: "left",
value: value ? format(value, "yyyy-MM-dd") : "Please choose a date",
color: value ? "grey.300" : "placeholder.300",
transition: "color 0s",
},
popoverCompProps: {
popoverContentProps: {
fontWeight: "400",
color: "grey.300",
},
},
});

const DatePicker = ({
name,
onChange,
value,
isDisabled = false,
}: DatePickerProps): ReactElement => {
const styles = useMemo(
() => getDatePickerStyles(value, isDisabled),
[value, isDisabled],
);

return (
<SingleDatepicker
configs={{
dayNames: "SMTWTFS".split(""),
monthNames: [
"January",
"February",
"March",
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December",
],
}}
configs={DATEPICKER_CONFIGS}
date={value || undefined}
name={name ?? "date-input"}
onDateChange={(date) => onChange(date)}
propsConfigs={{
dateNavBtnProps: {
fontWeight: 400,
},
dayOfMonthBtnProps: {
defaultBtnProps: {
width: "2rem",
color: "grey.400",
fontWeight: 400,
borderRadius: 20,
_hover: {
width: "2rem",
background: "blue.300",
color: "white",
bg: "blue.300",
},
},
selectedBtnProps: {
width: "2rem",
background: "blue.300",
borderRadius: 20,
color: "white",
},
todayBtnProps: {
width: "2rem",
borderColor: "grey.300",
borderRadius: 20,
borderWidth: "1px",
borderStyle: "solid",
},
},
inputProps: {
isDisabled,
type: "button",
cursor: "pointer",
"aria-label": "This is a date input, activate to open date picker",
textAlign: "left",
value: value ? format(value, "yyyy-MM-dd") : "Please choose a date",
color: value ? "grey.300" : "placeholder.300",
transition: "color 0s",
},
popoverCompProps: {
popoverContentProps: {
fontWeight: "400",
color: "grey.300",
},
},
}}
propsConfigs={styles}
/>
);
};
Expand Down
70 changes: 70 additions & 0 deletions frontend/src/components/common/form/DateTimePicker.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import type { ReactElement } from "react";
import React, { useRef, useState } from "react";
import { HStack } from "@chakra-ui/react";

import { combineDateAndTime } from "../../../utils/DateUtils";

import DatePicker from "./DatePicker";
import TimePicker from "./TimePicker";

type DateTimePickerProps = {
name?: string;
onChange: (date: Date | null) => void;
value: Date | null | undefined;
isDisabled?: boolean;
};

const DateTimePicker = ({
isDisabled,
onChange,
value,
name,
}: DateTimePickerProps): ReactElement => {
const timePickerRef = useRef<HTMLDivElement>(null);

const [internalDateValue, setInternalDateValue] = useState<Date | null>(null);
const [internalTimeValue, setInternalTimeValue] = useState<Date | null>(null);
const dateValue = value ?? internalDateValue;
const timeValue = value ?? internalTimeValue;

const handleDateChange = (newDate: Date | null) => {
setInternalDateValue(newDate);
if (timeValue && newDate) {
onChange(combineDateAndTime(newDate, timeValue));
}

// For some reason, the time picker doesn't focus properly if we click it
// while the date picker is open. This is a workaround to make sure the
// click happens after the date picker has started to close.
setTimeout(() => {
timePickerRef.current?.click();
}, 0);
};

const handleTimeChange = (newTime: Date | null) => {
setInternalTimeValue(newTime);
if (dateValue && newTime) {
onChange(combineDateAndTime(dateValue, newTime));
}
};

return (
<HStack gap={4}>
<DatePicker
isDisabled={isDisabled}
name={name}
onChange={handleDateChange}
value={dateValue ?? undefined}
/>
<TimePicker
ref={timePickerRef}
isDisabled={isDisabled}
name={name}
onChange={handleTimeChange}
value={timeValue ?? undefined}
/>
</HStack>
);
};

export default DateTimePicker;
Loading

0 comments on commit 7634bea

Please sign in to comment.