All files / src/features/ChangeLog/filter-components DateSelector.tsx

69.04% Statements 29/42
60.52% Branches 23/38
62.5% Functions 5/8
77.77% Lines 28/36

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106                            117x 5x 5x 5x   5x 5x         117x 2x       2x                   20x 20x 20x   20x 20x   20x 1x 1x     20x         20x 2x 2x   2x         2x 2x         2x 2x       20x         20x                                              
import { useRef, useState } from "react";
import moment from "moment";
import FilterItem from "./FilterItem";
import GiveDateRangePicker from "@shared/DateRangePicker/GiveDateRangePicker";
import { RangeValue } from "@shared/DateRangePicker/DateRangePicker.types";
import { formatForInput } from "@shared/DateRangePicker/utils";
import { DateValue } from "../types";
import { useCustomThemeV2 } from "@theme/hooks/useCustomThemeV2";
 
interface IDateSelectorProps {
  selectedDate: DateValue | undefined;
  onChange: (date: DateValue | undefined) => void;
}
 
const formatDateLabel = (date: DateValue): string => {
  const format = "MMM D, YYYY";
  const start = date.startDate ? moment(date.startDate).format(format) : "";
  const end = date.endDate ? moment(date.endDate).format(format) : "";
 
  Iif (start && end && start === end) return start;
  Eif (start && end) return `${start} - ${end}`;
  if (start) return start;
  return "";
};
 
const parseRangeToDateValue = (value: RangeValue): DateValue => {
  const { startDate: formattedStart, endDate: formattedEnd } = formatForInput(
    value,
    true,
  );
  return {
    startDate: moment(formattedStart, "MM/DD/YYYY").toDate(),
    endDate: moment(formattedEnd, "MM/DD/YYYY").toDate(),
  };
};
 
export default function DateSelector({
  selectedDate,
  onChange,
}: IDateSelectorProps) {
  const { isMobileView } = useCustomThemeV2();
  const [anchorEl, setAnchorEl] = useState<HTMLDivElement | null>(null);
  const clickCountRef = useRef(0);
 
  const isSelected = !!selectedDate;
  const label = isSelected ? formatDateLabel(selectedDate) : "Date";
 
  const handleOpen = (e: React.MouseEvent<HTMLElement>) => {
    clickCountRef.current = 0;
    setAnchorEl(e.currentTarget as HTMLDivElement);
  };
 
  const handleClear = (e: React.MouseEvent) => {
    e.stopPropagation();
    onChange(undefined);
  };
 
  const handleChange = (value: RangeValue | string) => {
    Iif (typeof value === "string") return;
    Iif (!value.startDate || !value.endDate) return;
 
    Iif (isMobileView) {
      // Mobile: don't apply here; apply only when user taps Apply (onApply)
      return;
    }
 
    clickCountRef.current += 1;
    Eif (
      clickCountRef.current >= 2 ||
      (value.startDate?.toString() !== value.endDate?.toString() &&
        value.endDate)
    ) {
      onChange(parseRangeToDateValue(value));
      setAnchorEl(null);
    }
  };
 
  const handleMobileApply = (range: RangeValue) => {
    if (!range.startDate || !range.endDate) return;
    onChange(parseRangeToDateValue(range));
  };
 
  return (
    <>
      <FilterItem
        isSelected={isSelected}
        title={label}
        onClick={handleOpen}
        onCancel={handleClear}
        isOpen={Boolean(anchorEl)}
      />
      {/* High z-index to ensure popover appears above GiveDraggableModal */}
      <GiveDateRangePicker
        value={selectedDate || { startDate: undefined, endDate: undefined }}
        onChange={handleChange}
        maxDate={new Date()}
        customAnchorEl={anchorEl}
        onClose={() => setAnchorEl(null)}
        popoverZIndex={1000000}
        useUTCMoment
        onApply={isMobileView ? handleMobileApply : undefined}
      />
    </>
  );
}