Skip to content

Commit

Permalink
feat(CalendarDay): add prop renderDayContent (#7518)
Browse files Browse the repository at this point in the history
## Описание

Необходимо добавить возможность отрисовки кастомного контента в ячейке дня календаря

## Изменения

Добавлен пропс `renderDayContent` для отрисовки контента

## Release notes

## Улучшения
- Calendar: Добавлен пропс `renderDayContent` для отрисовки кастомного контента в ячейке дня. Props также был добавлен в `CalendarRange`, `DateInput` и `DateRangeInput`
  • Loading branch information
EldarMuhamethanov authored Sep 10, 2024
1 parent 3ad055d commit ad82c81
Show file tree
Hide file tree
Showing 12 changed files with 104 additions and 8 deletions.
2 changes: 2 additions & 0 deletions packages/vkui/src/components/Calendar/Calendar.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { useArgs } from '@storybook/preview-api';
import type { Meta, StoryObj } from '@storybook/react';
import { CanvasFullLayout, DisableCartesianParam } from '../../storybook/constants';
import { createCalendarDayRenderField } from '../../testing/presets/createCalendarDayRenderField';
import { Calendar, type CalendarProps } from './Calendar';

const story: Meta<CalendarProps> = {
Expand All @@ -20,6 +21,7 @@ const story: Meta<CalendarProps> = {
shouldDisableDate: {
control: false,
},
renderDayContent: createCalendarDayRenderField(),
},
};

Expand Down
4 changes: 3 additions & 1 deletion packages/vkui/src/components/Calendar/Calendar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export interface CalendarProps
| 'prevMonthProps'
| 'nextMonthProps'
>,
Pick<CalendarDaysProps, 'dayProps' | 'listenDayChangesForUpdate'> {
Pick<CalendarDaysProps, 'dayProps' | 'listenDayChangesForUpdate' | 'renderDayContent'> {
value?: Date;
/**
* Запрещает выбор даты в прошлом.
Expand Down Expand Up @@ -109,6 +109,7 @@ export const Calendar = ({
nextMonthProps,
dayProps,
listenDayChangesForUpdate,
renderDayContent,
minDateTime,
maxDateTime,
...props
Expand Down Expand Up @@ -224,6 +225,7 @@ export const Calendar = ({
size={size}
dayProps={dayProps}
listenDayChangesForUpdate={listenDayChangesForUpdate}
renderDayContent={renderDayContent}
/>
{enableTime && value && size !== 's' && (
<div className={styles['Calendar__time']}>
Expand Down
10 changes: 10 additions & 0 deletions packages/vkui/src/components/CalendarDay/CalendarDay.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,14 @@ describe('CalendarDay', () => {
);
expect(screen.getByText('Children')).toBeInTheDocument();
});

it('check renderDayContent render', () => {
render(
<CalendarDayTest
renderDayContent={(day) => <span data-testid="day">{day.getDate()}</span>}
/>,
);
expect(screen.queryByTestId('day')).toBeTruthy();
expect(screen.queryByText('1')).toBeTruthy();
});
});
20 changes: 16 additions & 4 deletions packages/vkui/src/components/CalendarDay/CalendarDay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ export interface CalendarDayProps extends CalendarDayElementProps {
onChange: (value: Date) => void;
onEnter?: (value: Date) => void;
onLeave?: (value: Date) => void;
// Функция отрисовки контента в ячейке дня
renderDayContent?: (day: Date) => React.ReactNode;
}

export const CalendarDay: React.FC<CalendarDayProps> = React.memo(
Expand All @@ -52,6 +54,7 @@ export const CalendarDay: React.FC<CalendarDayProps> = React.memo(
size,
className,
children,
renderDayContent,
...restProps
}: CalendarDayProps) => {
const { locale } = useConfigProvider();
Expand All @@ -74,6 +77,18 @@ export const CalendarDay: React.FC<CalendarDayProps> = React.memo(
}
}, [focused]);

const content = React.useMemo(() => {
if (renderDayContent) {
return renderDayContent(day);
}
return (
<div className={styles['CalendarDay__day-number']}>
<VisuallyHidden>{children ?? label}</VisuallyHidden>
<span aria-hidden>{day.getDate()}</span>
</div>
);
}, [renderDayContent, day, children, label]);

if (hidden) {
return <div className={styles['CalendarDay__hidden']} />;
}
Expand Down Expand Up @@ -116,10 +131,7 @@ export const CalendarDay: React.FC<CalendarDayProps> = React.memo(
active && !disabled && styles['CalendarDay__inner--active'],
)}
>
<div className={styles['CalendarDay__day-number']}>
<VisuallyHidden>{children ?? label}</VisuallyHidden>
<span aria-hidden>{day.getDate()}</span>
</div>
{content}
</div>
</div>
</Tappable>
Expand Down
11 changes: 9 additions & 2 deletions packages/vkui/src/components/CalendarDays/CalendarDays.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,19 @@ import { useExternRef } from '../../hooks/useExternRef';
import { useTodayDate } from '../../hooks/useTodayDate';
import { getDaysNames, getWeeks } from '../../lib/calendar';
import type { HTMLAttributesWithRootRef } from '../../types';
import { CalendarDay, type CalendarDayElementProps } from '../CalendarDay/CalendarDay';
import {
CalendarDay,
type CalendarDayElementProps,
type CalendarDayProps,
} from '../CalendarDay/CalendarDay';
import { useConfigProvider } from '../ConfigProvider/ConfigProviderContext';
import { RootComponent } from '../RootComponent/RootComponent';
import { Footnote } from '../Typography/Footnote/Footnote';
import styles from './CalendarDays.module.css';

export interface CalendarDaysProps
extends Omit<HTMLAttributesWithRootRef<HTMLDivElement>, 'onChange'> {
extends Omit<HTMLAttributesWithRootRef<HTMLDivElement>, 'onChange'>,
Pick<CalendarDayProps, 'renderDayContent'> {
value?: Date | Array<Date | null>;
viewDate: Date;
weekStartsOn: 0 | 1 | 2 | 3 | 4 | 5 | 6;
Expand Down Expand Up @@ -55,6 +60,7 @@ export const CalendarDays = ({
dayProps,
listenDayChangesForUpdate = false,
getRootRef,
renderDayContent,
...props
}: CalendarDaysProps): React.ReactNode => {
const { locale } = useConfigProvider();
Expand Down Expand Up @@ -122,6 +128,7 @@ export const CalendarDays = ({
hinted={isDayHinted?.(day)}
sameMonth={sameMonth}
size={size}
renderDayContent={renderDayContent}
{...dayProps}
/>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { useArgs } from '@storybook/preview-api';
import type { Meta, StoryObj } from '@storybook/react';
import { CanvasFullLayout, DisableCartesianParam } from '../../storybook/constants';
import { createCalendarDayRenderField } from '../../testing/presets/createCalendarDayRenderField';
import { CalendarRange, type CalendarRangeProps } from './CalendarRange';

type StoryCalendarRangeProps = CalendarRangeProps & { startDate: number; endDate: number };
Expand Down Expand Up @@ -32,6 +33,7 @@ const story: Meta<StoryCalendarRangeProps> = {
},
control: { type: 'date' },
},
renderDayContent: createCalendarDayRenderField(),
},
};

Expand Down
5 changes: 4 additions & 1 deletion packages/vkui/src/components/CalendarRange/CalendarRange.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export interface CalendarRangeProps
| 'prevMonthIcon'
| 'nextMonthIcon'
>,
Pick<CalendarDaysProps, 'listenDayChangesForUpdate'> {
Pick<CalendarDaysProps, 'listenDayChangesForUpdate' | 'renderDayContent'> {
value?: DateRangeType;
disablePast?: boolean;
disableFuture?: boolean;
Expand Down Expand Up @@ -71,6 +71,7 @@ export const CalendarRange = ({
prevMonthIcon,
nextMonthIcon,
listenDayChangesForUpdate,
renderDayContent,
...props
}: CalendarRangeProps): React.ReactNode => {
const {
Expand Down Expand Up @@ -222,6 +223,7 @@ export const CalendarRange = ({
isHintedDaySelectionStart={isHintedDaySelectionStart}
isDayDisabled={isDayDisabled}
listenDayChangesForUpdate={listenDayChangesForUpdate}
renderDayContent={renderDayContent}
aria-label={changeDayLabel}
/>
</div>
Expand Down Expand Up @@ -260,6 +262,7 @@ export const CalendarRange = ({
isHintedDaySelectionStart={isHintedDaySelectionStart}
isDayDisabled={isDayDisabled}
listenDayChangesForUpdate={listenDayChangesForUpdate}
renderDayContent={renderDayContent}
tabIndex={0}
onBlur={resetSelectedDay}
/>
Expand Down
2 changes: 2 additions & 0 deletions packages/vkui/src/components/DateInput/DateInput.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { Meta, StoryObj } from '@storybook/react';
import { fn } from '@storybook/test';
import { CanvasFullLayout, DisableCartesianParam } from '../../storybook/constants';
import { createCalendarDayRenderField } from '../../testing/presets/createCalendarDayRenderField';
import { getFormFieldIconsPresets } from '../../testing/presets/getFormFieldIconsPresets';
import { DateInput, type DateInputProps } from './DateInput';

Expand All @@ -17,6 +18,7 @@ const story: Meta<DateInputProps> = {
},
after: iconsPresets,
before: iconsPresets,
renderDayContent: createCalendarDayRenderField(),
},
};

Expand Down
3 changes: 3 additions & 0 deletions packages/vkui/src/components/DateInput/DateInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ export interface DateInputProps
| 'nextMonthIcon'
| 'minDateTime'
| 'maxDateTime'
| 'renderDayContent'
>,
HasRootRef<HTMLDivElement>,
Omit<FormFieldProps, 'maxHeight'> {
Expand Down Expand Up @@ -145,6 +146,7 @@ export const DateInput = ({
prevMonthIcon,
nextMonthIcon,
disableCalendar = false,
renderDayContent,
...props
}: DateInputProps): React.ReactNode => {
const daysRef = React.useRef<HTMLSpanElement>(null);
Expand Down Expand Up @@ -330,6 +332,7 @@ export const DateInput = ({
changeYearLabel={changeYearLabel}
changeDayLabel={changeDayLabel}
showNeighboringMonth={showNeighboringMonth}
renderDayContent={renderDayContent}
size={size}
viewDate={viewDate}
onHeaderChange={onHeaderChange}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { useArgs } from '@storybook/preview-api';
import type { Meta, StoryObj } from '@storybook/react';
import { CanvasFullLayout, DisableCartesianParam } from '../../storybook/constants';
import { createCalendarDayRenderField } from '../../testing/presets/createCalendarDayRenderField';
import { getFormFieldIconsPresets } from '../../testing/presets/getFormFieldIconsPresets';
import { DateRangeInput, type DateRangeInputProps } from './DateRangeInput';

Expand Down Expand Up @@ -37,6 +38,7 @@ const story: Meta<StoryDateRangeInputProps> = {
},
before: iconsPresets,
after: iconsPresets,
renderDayContent: createCalendarDayRenderField(),
},
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ export interface DateRangeInputProps
| 'changeDayLabel'
| 'prevMonthIcon'
| 'nextMonthIcon'
| 'renderDayContent'
>,
HasRootRef<HTMLDivElement>,
Omit<FormFieldProps, 'maxHeight'> {
Expand Down Expand Up @@ -139,6 +140,7 @@ export const DateRangeInput = ({
prevMonthIcon,
nextMonthIcon,
disableCalendar = false,
renderDayContent,
...props
}: DateRangeInputProps): React.ReactNode => {
const daysStartRef = React.useRef<HTMLSpanElement>(null);
Expand Down Expand Up @@ -342,6 +344,7 @@ export const DateRangeInput = ({
changeDayLabel={changeDayLabel}
prevMonthIcon={prevMonthIcon}
nextMonthIcon={nextMonthIcon}
renderDayContent={renderDayContent}
/>
</Popper>
)}
Expand Down
48 changes: 48 additions & 0 deletions packages/vkui/src/testing/presets/createCalendarDayRenderField.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { Icon12Education } from '@vkontakte/icons';
import { Caption } from '../../components/Typography/Caption/Caption';
import { DisplayTitle } from '../../components/Typography/DisplayTitle/DisplayTitle';
import { Text } from '../../components/Typography/Text/Text';

export const createCalendarDayRenderField = () => {
return {
control: 'select' as const,
options: ['None', 'DayWithDayName', 'DayWithIcon'],
mapping: {
None: undefined,
DayWithDayName: (date: Date) => {
return (
<div
style={{
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
width: '100%',
}}
>
<DisplayTitle level="4" weight="2">
{date.getDate()}
</DisplayTitle>
<Caption caps level="2" weight="3">
{date.toLocaleDateString('ru-RU', { weekday: 'short' })}
</Caption>
</div>
);
},
DayWithIcon: (date: Date) => {
return (
<div
style={{
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
width: '100%',
}}
>
<Text>{date.getDate()}</Text>
<Icon12Education />
</div>
);
},
},
};
};

0 comments on commit ad82c81

Please sign in to comment.