Skip to content

Commit

Permalink
feat: refactor DateRangeCanlendar to use it as individual component
Browse files Browse the repository at this point in the history
affects: @medly-components/core
  • Loading branch information
gmukul01 committed Sep 5, 2023
1 parent d3ef445 commit 693b287
Show file tree
Hide file tree
Showing 6 changed files with 56 additions and 45 deletions.
Original file line number Diff line number Diff line change
@@ -1,28 +1,27 @@
import { KeyboardArrowLeftIcon, KeyboardArrowRightIcon } from '@medly-components/icons';
import { WithStyle } from '@medly-components/utils';
import type { FC } from 'react';
import { memo, useCallback, useEffect, useMemo, useState } from 'react';
import * as DatePickerStyled from '../../Calendar/Calendar.styled';
import { getMonthAndYearFromDate, getNextMonthAndYear, getPreviousMonthAndYear } from '../../Calendar/helper';
import MonthAndYearSelection from '../../Calendar/MonthAndYearSelection';
import { getMonthAndYearFromDate, getNextMonthAndYear, getPreviousMonthAndYear } from '../../Calendar/helper';
import * as Styled from './DateRangeCalendar.styled';
import Month from './Month';
import { CalendarAnimationTypes, Props } from './types';
import type { FC } from 'react';

const Component: FC<Props> = memo(props => {
const {
id,
size,
setActive,
placement,
selectedDates,
focusedElement,
focusElement,
setFocusedElement,
onFocusChange,
onDateSelection,
minSelectableDate,
maxSelectableDate,
withSingleMonth
withSingleMonth,
...restProps
} = props,
{ startDate, endDate } = selectedDates;

Expand All @@ -40,22 +39,19 @@ const Component: FC<Props> = memo(props => {
} else {
onDateSelection({ ...selectedDates, startDate: date });
}
setFocusedElement('END_DATE');
onFocusChange?.('END_DATE');
} else {
if (selectedDates.startDate && date <= selectedDates.startDate) {
onDateSelection({ startDate: date, endDate: selectedDates.startDate });
} else {
onDateSelection({ ...selectedDates, endDate: date });
}
setFocusedElement('START_DATE');
onFocusChange?.('START_DATE');
}
},
[selectedDates, focusedElement]
),
handleCalendarClick = useCallback(() => {
focusElement(focusedElement);
setActive(true);
}, [focusedElement]),
handleCalendarClick = useCallback(() => focusedElement && onFocusChange?.(focusedElement), [focusedElement]),
handleMonthAndYearChange = useCallback(
(val: { month: number; year: number }) => {
handleCalendarClick();
Expand All @@ -78,10 +74,10 @@ const Component: FC<Props> = memo(props => {
const commonProps = {
startDate,
endDate,
minSelectableDate,
maxSelectableDate,
hoveredDate,
setHoveredDate,
minSelectableDate: minSelectableDate!,
maxSelectableDate: maxSelectableDate!,
onChange: handleDateSelection
};

Expand All @@ -101,16 +97,23 @@ const Component: FC<Props> = memo(props => {
}, [selectedDates.startDate, selectedDates.endDate]);

return (
<Styled.DateRangeCalendar id={id} size={size} placement={placement} onClick={handleCalendarClick} withSingleMonth={withSingleMonth}>
<Styled.DateRangeCalendar
id={id}
size={size!}
placement={placement!}
onClick={handleCalendarClick}
withSingleMonth={withSingleMonth}
{...restProps}
>
<Styled.Header>
{withSingleMonth && (
<MonthAndYearSelection
id={id}
month={month}
year={year}
onChange={handleMonthAndYearChange}
minSelectableDate={minSelectableDate}
maxSelectableDate={maxSelectableDate}
minSelectableDate={minSelectableDate!}
maxSelectableDate={maxSelectableDate!}
/>
)}
<DatePickerStyled.MonthNavigation id={`${id}-navigation-backward`} onClick={handlePrevIconClick}>
Expand All @@ -132,5 +135,12 @@ const Component: FC<Props> = memo(props => {
</Styled.DateRangeCalendar>
);
});
Component.defaultProps = {
id: 'medly-date-range-calendar',
size: 'M',
placement: 'bottom-start',
minSelectableDate: new Date(1901, 0, 1),
maxSelectableDate: new Date(2100, 11, 1)
};
Component.displayName = 'DateRangeCalendar';
export const DateRangeCalendar: FC<Props> & WithStyle = Object.assign(Component, { Style: Styled.DateRangeCalendar });
Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@
import { HTMLProps } from '@medly-components/utils';
import { Placement } from '../../Popover/types';
import { DateRangeProps } from '../types';
import { DateRangeProps, FOCUS_ELEMENT } from '../types';

export type CalendarAnimationTypes = 'move-out-left' | 'move-out-right' | 'move-in-left' | 'move-in-right';

export type Props = {
id: string;
size: 'S' | 'M';
placement: Placement;
export type Props = Omit<HTMLProps<HTMLDivElement>, 'size'> & {
id?: string;
size?: 'S' | 'M';
placement?: Placement;
selectedDates: Required<DateRangeProps>['value'];
minSelectableDate: Date;
maxSelectableDate: Date;
withSingleMonth?: boolean;
focusedElement: 'START_DATE' | 'END_DATE';
onDateSelection: Required<DateRangeProps>['onChange'];
setFocusedElement: React.Dispatch<React.SetStateAction<'START_DATE' | 'END_DATE'>>;
setActive: (val: boolean) => void;
focusElement: (element: 'START_DATE' | `END_DATE`) => void;
minSelectableDate?: Date;
maxSelectableDate?: Date;
withSingleMonth?: boolean;
focusedElement?: FOCUS_ELEMENT;
onFocusChange?: (element: FOCUS_ELEMENT) => void;
};
23 changes: 11 additions & 12 deletions packages/core/src/components/DateRangePicker/DateRangePicker.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { useKeyPress, useOuterClickNotifier, useUpdateEffect } from '@medly-components/utils';
import { useKeyPress, useOuterClickNotifier } from '@medly-components/utils';
import { FC, memo, useCallback, useEffect, useMemo, useRef, useState } from 'react';
import * as TextFieldStyled from '../TextField/Styled';
import CustomDateRangeOptions from './CustomDateRangeOptions';
import DateRangeCalendar from './DateRangeCalendar';
import DateRangeTextFields from './DateRangeTextFields';
import { dateRangeHelpers } from './helpers/dateRangeHelpers';
import { DateRangeProps, DateRangeSelectionEnum, PopoverTypes } from './types';
import { DateRangeProps, DateRangeSelectionEnum, FOCUS_ELEMENT, PopoverTypes } from './types';

export const DateRangePicker: FC<DateRangeProps> = memo(props => {
const {
Expand Down Expand Up @@ -42,7 +42,7 @@ export const DateRangePicker: FC<DateRangeProps> = memo(props => {
outerClickValidator = useRef<(e: MouseEvent) => void>(null),
[isActive, setActive] = useState(false),
[activePopover, setActivePopover] = useState<PopoverTypes>(PopoverTypes.CALENDAR),
[focusedElement, setFocusedElement] = useState<'START_DATE' | `END_DATE`>('START_DATE'),
[focusedElement, setFocusedElement] = useState<FOCUS_ELEMENT>('START_DATE'),
focusElement = useCallback(element => (element === 'START_DATE' ? startDateRef : endDateRef).current?.focus(), []),
isTabKeyPressed = useKeyPress('Tab', true, wrapperRef),
wrapperMinWidth = useMemo(
Expand All @@ -63,10 +63,14 @@ export const DateRangePicker: FC<DateRangeProps> = memo(props => {
setActivePopover(PopoverTypes.CALENDAR);
setActive(valueToSet);
}, []),
onFocusChange = useCallback((element: FOCUS_ELEMENT) => {
focusElement(element);
setFocusedElement(element);
}, []),
onOptionClick = useCallback(
(option: any) => {
if (option.value === DateRangeSelectionEnum.CUSTOM) {
focusElement('START_DATE');
onFocusChange('START_DATE');
setActivePopover(PopoverTypes.CALENDAR);
setActive(true);
} else {
Expand All @@ -82,17 +86,14 @@ export const DateRangePicker: FC<DateRangeProps> = memo(props => {
setActive(false);
}
},
[onChange, focusElement]
[onChange]
);

useOuterClickNotifier((e: MouseEvent) => {
setActive(false);
isActive && onPopupClose && onPopupClose();
isActive && outerClickValidator.current && outerClickValidator.current(e);
}, wrapperRef);

useUpdateEffect(() => focusElement(focusedElement), [focusedElement]);

useEffect(() => {
const activeElement = document.activeElement as HTMLInputElement;
isTabKeyPressed && !(activeElement.name === 'START_DATE' || activeElement.name === 'END_DATE') && setActive(false);
Expand Down Expand Up @@ -125,7 +126,7 @@ export const DateRangePicker: FC<DateRangeProps> = memo(props => {
onDateChange={onChange}
displayFormat={displayFormat!}
onCalendarIconClick={onCalendarIconClick}
setFocusedElement={setFocusedElement}
setFocusedElement={onFocusChange}
startDateRef={startDateRef}
endDateRef={endDateRef}
showTooltipForHelperAndErrorText={showTooltipForHelperAndErrorText}
Expand All @@ -141,12 +142,10 @@ export const DateRangePicker: FC<DateRangeProps> = memo(props => {
size={size!}
placement={popoverPlacement!}
selectedDates={value}
setActive={onCalendarIconClick}
withSingleMonth={withSingleMonth}
focusElement={focusElement}
onDateSelection={onChange}
focusedElement={focusedElement}
setFocusedElement={setFocusedElement}
onFocusChange={onFocusChange}
minSelectableDate={minSelectableDate!}
maxSelectableDate={maxSelectableDate!}
/>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { ChangeEvent, Dispatch, RefObject, SetStateAction } from 'react';
import { DateRangeProps, DateRangeType, PopoverTypes } from '../types';
import type { ChangeEvent, RefObject } from 'react';
import { DateRangeProps, DateRangeType, FOCUS_ELEMENT, PopoverTypes } from '../types';

export type Props = {
id: string;
Expand All @@ -19,7 +19,7 @@ export type Props = {
onDateChange: Required<DateRangeProps>['onChange'];
displayFormat: Required<DateRangeProps>['displayFormat'];
onCalendarIconClick: (val: boolean) => void;
setFocusedElement: Dispatch<SetStateAction<'START_DATE' | 'END_DATE'>>;
setFocusedElement: (element: FOCUS_ELEMENT) => void;
startDateRef: RefObject<HTMLInputElement>;
endDateRef: RefObject<HTMLInputElement>;
validator?: (val: DateRangeType, eventType: ChangeEvent<HTMLInputElement>) => string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { ChangeEvent, FormEvent, useCallback, useEffect, useMemo, useState } fro
import { isValidDate } from '../../Calendar/helper';
import getMaskedValue from '../../TextField/getMaskedValue';
import { getFormattedDate } from '../helpers';
import { FOCUS_ELEMENT } from '../types';
import { Props } from './types';

export const useDateRangeTextFieldsHandlers = (props: Props) => {
Expand Down Expand Up @@ -42,7 +43,7 @@ export const useDateRangeTextFieldsHandlers = (props: Props) => {
),
onTextFieldFocus = useCallback((event: React.FocusEvent<HTMLInputElement>) => {
onCalendarIconClick(true);
setFocusedElement(event.target.name as `START_DATE` | `END_DATE`);
setFocusedElement(event.target.name as FOCUS_ELEMENT);
event.target.setSelectionRange(event.target.value.length, event.target.value.length);
}, []),
handleTextChange = useCallback(
Expand Down
2 changes: 2 additions & 0 deletions packages/core/src/components/DateRangePicker/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ export enum DateRangeSelectionEnum {
QUARTER = 'QUARTER'
}

export type FOCUS_ELEMENT = 'START_DATE' | 'END_DATE';

export enum PopoverTypes {
CALENDAR = 'CALENDAR',
CUSTOM_RANGE_OPTIONS = 'CUSTOM_RANGE_OPTIONS'
Expand Down

0 comments on commit 693b287

Please sign in to comment.