From 41264e2e3435e42862e4e37eb3c1cf11208f227e Mon Sep 17 00:00:00 2001 From: Zach Silveira Date: Tue, 27 Apr 2021 17:53:28 -0400 Subject: [PATCH] Refactor to make the Monthly Day component composable (#4) * Refactor to make the Monthly Day component composable * Fix a few more missing pieces * remove key * Export handleOmittedDays * bump version --- .gitignore | 3 +- README.md | 31 +++++--- package.json | 2 +- src/Monthly/MonthlyBody.tsx | 135 ++++++++++++++++++++++++++++++++ src/Monthly/MonthlyCalendar.tsx | 111 -------------------------- src/index.tsx | 1 + stories/Monthly.stories.tsx | 70 ++++++++++------- test/Monthly/TestComponents.tsx | 27 ++++--- test/Weekly/Events.test.tsx | 19 +++-- 9 files changed, 223 insertions(+), 176 deletions(-) create mode 100644 src/Monthly/MonthlyBody.tsx diff --git a/.gitignore b/.gitignore index 07cac22..3ca1d91 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,5 @@ node_modules .cache dist -.parcel-cache \ No newline at end of file +.parcel-cache +coverage \ No newline at end of file diff --git a/README.md b/README.md index a8d2ae7..04a6e5a 100644 --- a/README.md +++ b/README.md @@ -21,9 +21,10 @@ npm install @zach.codes/react-calendar date-fns [See this code in action](https://calendar.zach.codes/?path=/story/monthly-calendar--basic-monthly-calendar) ```tsx -import {format, subHours, startOfMonth} from 'date-fns' +import { format, subHours, startOfMonth } from 'date-fns'; import { MonthlyBody, + MonthlyDay, MonthlyCalendar, MonthlyNav, DefaultMonthlyEventItem, @@ -46,17 +47,20 @@ export const MyMonthlyCalendar = () => { { title: 'Call John', date: subHours(new Date(), 1) }, { title: 'Meeting with Bob', date: new Date() }, ]} - renderDay={data => - data.map((item, index) => ( - - )) - } - /> + > + + renderDay={data => + data.map((item, index) => ( + + )) + } + /> + ); }; @@ -105,6 +109,9 @@ No props at this time - `omitDays` lets you hide certain days from the calendar, for instance, hide Saturday and Sunday. Days are represented as 0-6, as seen in the [date-fns](https://date-fns.org/v2.19.0/docs/getDay#returns) documentation. Hiding Monday would be `omitDays={[1]}` Hiding the weekend would be `omitDays={[0, 6]}` - `events` this is an array of events, the only thing required inside each object is a `date` field with a Date object representing the exact time of the event + +`MonthlyDay` + - `renderDay` callback function that is passed an array of events for each day displayed, letting you render the events for the day ## WeeklyCalendar diff --git a/package.json b/package.json index af17044..8ca2b6a 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "version": "0.0.3", + "version": "0.1.0", "license": "MIT", "name": "@zach.codes/react-calendar", "main": "dist/index.js", diff --git a/src/Monthly/MonthlyBody.tsx b/src/Monthly/MonthlyBody.tsx new file mode 100644 index 0000000..84d0a3f --- /dev/null +++ b/src/Monthly/MonthlyBody.tsx @@ -0,0 +1,135 @@ +import React, { ReactNode, useContext } from 'react'; +import { useMonthlyCalendar } from './MonthlyCalendar'; +import { daysInWeek } from '../shared'; +import { format, getDay, isSameDay } from 'date-fns'; + +const MonthlyBodyContext = React.createContext({} as any); +type BodyState = { + day: Date; + events: DayData[]; +}; + +export function useMonthlyBody() { + return useContext>(MonthlyBodyContext); +} + +type OmittedDaysProps = { + days: Date[]; + omitDays?: number[]; +}; + +export const handleOmittedDays = ({ days, omitDays }: OmittedDaysProps) => { + let headings = daysInWeek; + let daysToRender = days; + + //omit the headings and days of the week that were passed in + if (omitDays) { + headings = daysInWeek.filter(day => !omitDays.includes(day.day)); + daysToRender = days.filter(day => !omitDays.includes(getDay(day))); + } + + // omit the padding if an omitted day was before the start of the month + let firstDayOfMonth = getDay(daysToRender[0]) as number; + if (omitDays) { + let subtractOmittedDays = omitDays.filter(day => day < firstDayOfMonth) + .length; + firstDayOfMonth = firstDayOfMonth - subtractOmittedDays; + } + let padding = new Array(firstDayOfMonth).fill(0); + + return { headings, daysToRender, padding }; +}; + +//to prevent these from being purged in production, we make a lookup object +const headingClasses = { + l3: 'lg:grid-cols-3', + l4: 'lg:grid-cols-4', + l5: 'lg:grid-cols-5', + l6: 'lg:grid-cols-6', + l7: 'lg:grid-cols-7', +}; + +type MonthlyBodyProps = { + /* + skip days, an array of days, starts at sunday (0), saturday is 6 + ex: [0,6] would remove sunday and saturday from rendering + */ + omitDays?: number[]; + events: (DayData & { date: Date })[]; + children: ReactNode; +}; + +export function MonthlyBody({ + omitDays, + events, + children, +}: MonthlyBodyProps) { + let { days } = useMonthlyCalendar(); + let { headings, daysToRender, padding } = handleOmittedDays({ + days, + omitDays, + }); + + let headingClassName = 'border-b-2 p-2 border-r-2 lg:block hidden'; + return ( +
+
+ {headings.map(day => ( +
+ {day.label} +
+ ))} + {padding.map((_, index) => ( +
+ ))} + {daysToRender.map(day => ( + isSameDay(data.date, day)), + }} + > + {children} + + ))} +
+
+ ); +} + +type MonthlyDayProps = { + renderDay: (events: DayData[]) => ReactNode; +}; +export function MonthlyDay({ renderDay }: MonthlyDayProps) { + let { day, events } = useMonthlyBody(); + let dayNumber = format(day, 'd'); + + return ( +
+
+
{dayNumber}
+
{format(day, 'EEEE')}
+
+
    + {renderDay(events)} +
+
+ ); +} diff --git a/src/Monthly/MonthlyCalendar.tsx b/src/Monthly/MonthlyCalendar.tsx index 63e4e66..62d8db2 100644 --- a/src/Monthly/MonthlyCalendar.tsx +++ b/src/Monthly/MonthlyCalendar.tsx @@ -6,11 +6,8 @@ import { subMonths, addMonths, getYear, - getDay, - isSameDay, } from 'date-fns'; import React, { ReactNode, useContext } from 'react'; -import { daysInWeek } from '../shared'; type CalendarState = { days: Date[]; @@ -80,111 +77,3 @@ export const MonthlyNav = () => {
); }; -type ExtraMonthData = { date: Date }; - -type MonthlyBodyProps = { - /* - skip days, an array of days, starts at sunday (0), saturday is 6 - ex: [0,6] would remove sunday and saturday from rendering - */ - omitDays?: number[]; - events: (MonthData & ExtraMonthData)[]; - - renderDay: (data: (MonthData & ExtraMonthData)[]) => ReactNode; -}; - -type OmittedDaysProps = { - days: Date[]; - omitDays?: number[]; -}; - -const handleOmittedDays = ({ days, omitDays }: OmittedDaysProps) => { - let headings = daysInWeek; - let daysToRender = days; - - //omit the headings and days of the week that were passed in - if (omitDays) { - headings = daysInWeek.filter(day => !omitDays.includes(day.day)); - daysToRender = days.filter(day => !omitDays.includes(getDay(day))); - } - - // omit the padding if an omitted day was before the start of the month - let firstDayOfMonth = getDay(daysToRender[0]) as number; - if (omitDays) { - let subtractOmittedDays = omitDays.filter(day => day < firstDayOfMonth) - .length; - firstDayOfMonth = firstDayOfMonth - subtractOmittedDays; - } - let padding = new Array(firstDayOfMonth).fill(0); - - return { headings, daysToRender, padding }; -}; - -//to prevent these from being purged in production, we make a lookup object -const headingClasses = { - l3: 'lg:grid-cols-3', - l4: 'lg:grid-cols-4', - l5: 'lg:grid-cols-5', - l6: 'lg:grid-cols-6', - l7: 'lg:grid-cols-7', -}; - -export function MonthlyBody({ - omitDays, - events, - renderDay, -}: MonthlyBodyProps) { - let { days } = useMonthlyCalendar(); - let { headings, daysToRender, padding } = handleOmittedDays({ - days, - omitDays, - }); - - let headingClassName = 'border-b-2 p-2 border-r-2 lg:block hidden'; - return ( -
-
- {headings.map(day => ( -
- {day.label} -
- ))} - {padding.map((_, index) => ( -
- ))} - {daysToRender.map(day => { - let dayData = events.filter(data => isSameDay(data.date, day)); - let dayNumber = format(day, 'd'); - return ( -
-
-
{dayNumber}
-
{format(day, 'EEEE')}
-
-
    - {renderDay(dayData)} -
-
- ); - })} -
-
- ); -} diff --git a/src/index.tsx b/src/index.tsx index 179d40c..96c3d03 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -1,4 +1,5 @@ export * from './Monthly/MonthlyCalendar'; +export * from './Monthly/MonthlyBody'; export * from './Monthly/MonthlyEventItems'; export * from './Weekly/WeeklyCalendar'; diff --git a/stories/Monthly.stories.tsx b/stories/Monthly.stories.tsx index 6c5dc3b..87be968 100644 --- a/stories/Monthly.stories.tsx +++ b/stories/Monthly.stories.tsx @@ -6,6 +6,7 @@ import { MonthlyCalendar, MonthlyNav, DefaultMonthlyEventItem, + MonthlyDay, } from '../src'; import '../src/tailwind.css'; import { EventType, events } from './dummyEvents'; @@ -28,16 +29,19 @@ export const BasicMonthlyCalendar: Story = args => { { title: 'Call John', date: subHours(new Date(), 1) }, { title: 'Meeting with Bob', date: new Date() }, ]} - renderDay={data => - data.map((item, index) => ( - - )) - } - /> + > + + renderDay={data => + data.map((item, index) => ( + + )) + } + /> + ); }; @@ -61,16 +65,19 @@ export const MyMonthlyCalendar: Story = args => { - data.map((item, index) => ( - - )) - } - /> + > + + renderDay={data => + data.map((item, index) => ( + + )) + } + /> + ); }; @@ -116,16 +123,19 @@ export const AsyncEvents: Story = args => { - data.map((item, index) => ( - - )) - } - /> + > + + renderDay={data => + data.map((item, index) => ( + + )) + } + /> + ); }; diff --git a/test/Monthly/TestComponents.tsx b/test/Monthly/TestComponents.tsx index ca0ef77..ca6a7f7 100644 --- a/test/Monthly/TestComponents.tsx +++ b/test/Monthly/TestComponents.tsx @@ -4,6 +4,7 @@ import { DefaultMonthlyEventItem, MonthlyBody, MonthlyCalendar, + MonthlyDay, MonthlyNav, } from '../../src'; @@ -26,19 +27,19 @@ export const MonthlyCalendarTest = ({ onCurrentMonthChange={onCurrentMonthChange} > - - data.map((item, index) => ( - - )) - } - /> + + + renderDay={data => + data.map((item, index) => ( + + )) + } + /> + ); }; diff --git a/test/Weekly/Events.test.tsx b/test/Weekly/Events.test.tsx index 9b2c982..9def938 100644 --- a/test/Weekly/Events.test.tsx +++ b/test/Weekly/Events.test.tsx @@ -2,6 +2,7 @@ import React from 'react'; import { screen, render, fireEvent } from '@testing-library/react'; import { addDays, subDays, subHours } from 'date-fns'; import { WeeklyCalendarTest } from './TestComponents'; +import { WeeklyResponsiveContainer } from '../../src'; let testDate = '2021-03-03T00:39:27.448Z'; test('Renders full week initially', () => { @@ -48,14 +49,16 @@ test('Hides event from next week', () => { test('Renders single day after click', () => { render( - + + + ); fireEvent.click(screen.getByText('Sunday 28th'));