feat: add customization calendar widget
This commit is contained in:
parent
29c1bce814
commit
c950bc6757
@ -1 +1,2 @@
|
|||||||
|
export type { CalendarWidgetProps } from './ui/CalendarWidget.ui';
|
||||||
export { default as CalendarWidget } from './ui/CalendarWidget.ui';
|
export { default as CalendarWidget } from './ui/CalendarWidget.ui';
|
||||||
|
|||||||
@ -1,10 +1,15 @@
|
|||||||
|
import type { DataDateString } from 'shared/types/date';
|
||||||
|
|
||||||
import classes from './CalendarWidget.module.css';
|
import classes from './CalendarWidget.module.css';
|
||||||
import { CalendarWidgetProvider } from './CalendarWidgetContext';
|
import { CalendarWidgetProvider } from './CalendarWidgetContext';
|
||||||
import { DateWrapper } from './components/DateWrapper';
|
import { DateWrapper } from './components/DateWrapper';
|
||||||
import { DayOfWeekRow } from './components/DayOfWeekRow';
|
import { DayOfWeekRow } from './components/DayOfWeekRow';
|
||||||
import { HeaderRow } from './components/HeaderRow';
|
import { HeaderRow } from './components/HeaderRow';
|
||||||
|
|
||||||
import type { FC } from 'react';
|
import type { FC, HTMLAttributes } from 'react';
|
||||||
|
|
||||||
|
type DateCellPropsType = HTMLAttributes<HTMLDivElement>;
|
||||||
|
type DateCellPropsFuncType = (params: { date: Date; dataDate: DataDateString }) => DateCellPropsType;
|
||||||
|
|
||||||
export type CalendarWidgetProps = {
|
export type CalendarWidgetProps = {
|
||||||
maxDate?: Date;
|
maxDate?: Date;
|
||||||
@ -13,6 +18,8 @@ export type CalendarWidgetProps = {
|
|||||||
locale?: 'en' | 'ru';
|
locale?: 'en' | 'ru';
|
||||||
highlightToday?: boolean;
|
highlightToday?: boolean;
|
||||||
highlightValue?: boolean;
|
highlightValue?: boolean;
|
||||||
|
|
||||||
|
DateCellProps?: DateCellPropsFuncType | DateCellPropsType;
|
||||||
};
|
};
|
||||||
const CalendarWidget: FC<CalendarWidgetProps> = (props) => (
|
const CalendarWidget: FC<CalendarWidgetProps> = (props) => (
|
||||||
<CalendarWidgetProvider {...props}>
|
<CalendarWidgetProvider {...props}>
|
||||||
|
|||||||
@ -4,11 +4,16 @@ import React, { createContext, useMemo, useState } from 'react';
|
|||||||
import type { CalendarWidgetProps } from './CalendarWidget.ui';
|
import type { CalendarWidgetProps } from './CalendarWidget.ui';
|
||||||
import type { FC, PropsWithChildren } from 'react';
|
import type { FC, PropsWithChildren } from 'react';
|
||||||
|
|
||||||
type CalendarWidgetContextValue = Required<CalendarWidgetProps> & {
|
type CalendarWidgetContextValue = Omit<CalendarWidgetProps, 'locale' | 'maxDate' | 'minDate' | 'value'> & {
|
||||||
targetYear: number;
|
targetYear: number;
|
||||||
targetMonthIndex: number;
|
targetMonthIndex: number;
|
||||||
targetDate: Date;
|
targetDate: Date;
|
||||||
setTargetDate: React.Dispatch<React.SetStateAction<Date>>;
|
setTargetDate: React.Dispatch<React.SetStateAction<Date>>;
|
||||||
|
|
||||||
|
maxDate: NonNullable<CalendarWidgetProps['maxDate']>;
|
||||||
|
minDate: NonNullable<CalendarWidgetProps['minDate']>;
|
||||||
|
locale: NonNullable<CalendarWidgetProps['locale']>;
|
||||||
|
value: NonNullable<CalendarWidgetProps['value']>;
|
||||||
};
|
};
|
||||||
export const CalendarWidgetContext = createContext({} as CalendarWidgetContextValue);
|
export const CalendarWidgetContext = createContext({} as CalendarWidgetContextValue);
|
||||||
|
|
||||||
@ -21,8 +26,6 @@ export const CalendarWidgetProvider: FC<CalendarWidgetProviderProps> = (props) =
|
|||||||
minDate = new Date(1990, 0, 1),
|
minDate = new Date(1990, 0, 1),
|
||||||
locale = 'ru',
|
locale = 'ru',
|
||||||
value = new Date(),
|
value = new Date(),
|
||||||
highlightToday = false,
|
|
||||||
highlightValue = false,
|
|
||||||
...restProps
|
...restProps
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
@ -37,10 +40,8 @@ export const CalendarWidgetProvider: FC<CalendarWidgetProviderProps> = (props) =
|
|||||||
maxDate,
|
maxDate,
|
||||||
locale,
|
locale,
|
||||||
value,
|
value,
|
||||||
highlightToday,
|
|
||||||
highlightValue,
|
|
||||||
...restProps,
|
...restProps,
|
||||||
}), [highlightValue, targetDate, minDate, maxDate, locale, value, highlightToday, restProps]);
|
}), [targetDate, minDate, maxDate, locale, value, restProps]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CalendarWidgetContext.Provider value={contextValue}>
|
<CalendarWidgetContext.Provider value={contextValue}>
|
||||||
|
|||||||
@ -32,6 +32,6 @@ describe('Base Render', () => {
|
|||||||
setup(new Date(2024, 3, 15));
|
setup(new Date(2024, 3, 15));
|
||||||
|
|
||||||
const dateCellElem = screen.getByTestId('date-cell');
|
const dateCellElem = screen.getByTestId('date-cell');
|
||||||
expect(dateCellElem).toHaveAttribute('data-date', '4/15/2024');
|
expect(dateCellElem).toHaveAttribute('data-date', '04/15/2024');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,47 +1,77 @@
|
|||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import { useContext } from 'react';
|
import React, { useContext, useMemo, useRef } from 'react';
|
||||||
|
|
||||||
|
import { getDataDateString } from 'shared/utils/date';
|
||||||
|
|
||||||
import { CalendarWidgetContext } from '../../../CalendarWidgetContext';
|
import { CalendarWidgetContext } from '../../../CalendarWidgetContext';
|
||||||
|
|
||||||
import classes from './DateCell.style.module.css';
|
import classes from './DateCell.style.module.css';
|
||||||
|
|
||||||
import type { FC } from 'react';
|
import type { FC, MouseEventHandler, KeyboardEventHandler } from 'react';
|
||||||
|
|
||||||
type DateCellProps = {
|
type DateCellProps = React.HTMLAttributes<HTMLDivElement> & {
|
||||||
date: Date;
|
date: Date;
|
||||||
};
|
};
|
||||||
const DateCell: FC<DateCellProps> = (props) => {
|
const DateCell: FC<DateCellProps> = (props) => {
|
||||||
const {
|
const {
|
||||||
date,
|
date,
|
||||||
} = props;
|
} = props;
|
||||||
const format = 'en-En';
|
const rootDateCellRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
targetMonthIndex,
|
targetMonthIndex,
|
||||||
value,
|
value,
|
||||||
highlightToday,
|
highlightToday,
|
||||||
highlightValue,
|
highlightValue,
|
||||||
|
DateCellProps = {},
|
||||||
} = useContext(CalendarWidgetContext);
|
} = useContext(CalendarWidgetContext);
|
||||||
|
|
||||||
const todayDate = new Date();
|
const todayDate = new Date();
|
||||||
const dataDate = new Intl.DateTimeFormat(format).format(date);
|
const dataDate = getDataDateString(date);
|
||||||
const dataToday = new Intl.DateTimeFormat(format).format(todayDate);
|
const dataToday = getDataDateString(todayDate);
|
||||||
const dataValue = new Intl.DateTimeFormat(format).format(value);
|
const dataValue = getDataDateString(value);
|
||||||
const isOutMonthRange = date.getMonth() !== targetMonthIndex;
|
const isOutMonthRange = date.getMonth() !== targetMonthIndex;
|
||||||
|
|
||||||
|
const {
|
||||||
|
className,
|
||||||
|
onClick,
|
||||||
|
onKeyPress,
|
||||||
|
...restProps
|
||||||
|
} = useMemo(() => {
|
||||||
|
if (typeof DateCellProps === 'function') {
|
||||||
|
return DateCellProps({ date, dataDate });
|
||||||
|
}
|
||||||
|
|
||||||
|
return DateCellProps;
|
||||||
|
// @ts-ignore
|
||||||
|
}, [DateCellProps, dataDate, date]);
|
||||||
|
|
||||||
|
const onClickHandler: MouseEventHandler<HTMLDivElement> = (event) => {
|
||||||
|
onClick?.({ ...event, target: rootDateCellRef.current as EventTarget });
|
||||||
|
};
|
||||||
|
|
||||||
|
const onKeyPressHandler: KeyboardEventHandler<HTMLDivElement> = (event) => {
|
||||||
|
onKeyPress?.(event);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={dataDate}
|
key={dataDate}
|
||||||
className={classes.dateCell}
|
ref={rootDateCellRef}
|
||||||
|
className={classnames(classes.dateCell, className)}
|
||||||
data-date={dataDate}
|
data-date={dataDate}
|
||||||
data-testid="date-cell"
|
data-testid="date-cell"
|
||||||
|
role="button"
|
||||||
|
tabIndex={0}
|
||||||
|
onClick={onClickHandler}
|
||||||
|
onKeyPress={onKeyPressHandler}
|
||||||
|
{...restProps}
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
className={classnames(
|
className={classnames(
|
||||||
{
|
{
|
||||||
[classes.today as string]: highlightToday && dataDate === dataToday,
|
[classes.today as string]: Boolean(highlightToday) && dataDate === dataToday,
|
||||||
[classes.outMothRange as string]: isOutMonthRange,
|
[classes.outMothRange as string]: isOutMonthRange,
|
||||||
[classes.valueDate as string]: highlightValue && dataDate === dataValue,
|
[classes.valueDate as string]: Boolean(highlightValue) && dataDate === dataValue,
|
||||||
},
|
},
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
|
|||||||
@ -33,7 +33,7 @@ describe('Test Date Row', () => {
|
|||||||
setupWeek();
|
setupWeek();
|
||||||
|
|
||||||
const dateRowElem = screen.getByTestId('date-row');
|
const dateRowElem = screen.getByTestId('date-row');
|
||||||
expect(dateRowElem).toHaveAttribute('data-row', '4/15/2024-4/21/2024');
|
expect(dateRowElem).toHaveAttribute('data-row', '04/15/2024-04/21/2024');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Correct style class', () => {
|
test('Correct style class', () => {
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
import { getDataDateString } from 'shared/utils/date';
|
||||||
|
|
||||||
import { DateCell } from '../../DateCell';
|
import { DateCell } from '../../DateCell';
|
||||||
|
|
||||||
import classes from './DateRow.style.module.css';
|
import classes from './DateRow.style.module.css';
|
||||||
@ -12,16 +14,21 @@ const DateRow: FC<DateRowType> = (props) => {
|
|||||||
cells,
|
cells,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const format = 'en-En';
|
const firstDate = cells[0];
|
||||||
|
const lastDate = cells[cells.length - 1];
|
||||||
|
|
||||||
const firstDate = new Intl.DateTimeFormat(format).format(cells[0]);
|
if (!firstDate || !lastDate) {
|
||||||
const lastDate = new Intl.DateTimeFormat(format).format(cells[cells.length - 1]);
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const firstDataDate = getDataDateString(firstDate);
|
||||||
|
const lastDataDate = getDataDateString(lastDate);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={`${firstDate}-${lastDate}`}
|
key={`${firstDataDate}-${lastDataDate}`}
|
||||||
className={classes.dateRow}
|
className={classes.dateRow}
|
||||||
data-row={`${firstDate}-${lastDate}`}
|
data-row={`${firstDataDate}-${lastDataDate}`}
|
||||||
data-testid="date-row"
|
data-testid="date-row"
|
||||||
>
|
>
|
||||||
{cells.map((item) => (
|
{cells.map((item) => (
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user