diff --git a/.husky/pre-commit b/.husky/pre-commit index 660b8209..d1096ab1 100755 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1,5 +1,4 @@ #!/bin/sh . "$(dirname "$0")/_/husky.sh" -npm run lint npm run test diff --git a/.vscode/settings.json b/.vscode/settings.json index 7bcc48c8..cbdc803e 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,4 +1,4 @@ { - "jest.jestCommandLine": "npm run test --", + "jest.jestCommandLine": "npm run test -- --", "cSpell.words": ["Schedulely"] } diff --git a/package-lock.json b/package-lock.json index b4937f5a..93f1a347 100644 --- a/package-lock.json +++ b/package-lock.json @@ -50,7 +50,7 @@ "rollup-plugin-progress": "^1.1.2", "rollup-plugin-ts": "^3.0.2", "sass": "^1.55.0", - "turbo": "1.6.1", + "turbo": "1.6.2", "typescript": "^4.8.3" }, "engines": { @@ -18849,27 +18849,27 @@ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" }, "node_modules/turbo": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/turbo/-/turbo-1.6.1.tgz", - "integrity": "sha512-CkcJo17cbwfTzmxtxJo2AbbeVqaz1yQotBUqVwZDdcrVSNKci2nvw+JHJ3sy/z9YY9xOJmoRaZifbkja3UXUWA==", + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/turbo/-/turbo-1.6.2.tgz", + "integrity": "sha512-a6UM9HaAjM5ai+vxDFI/z0l4Bf6zWjf7wCf9Ip2iyd4XZkZZnhRtM6FpaUWzorjz9Dsqnfwu3nkWY3wPdH02IQ==", "dev": true, "hasInstallScript": true, "bin": { "turbo": "bin/turbo" }, "optionalDependencies": { - "turbo-darwin-64": "1.6.1", - "turbo-darwin-arm64": "1.6.1", - "turbo-linux-64": "1.6.1", - "turbo-linux-arm64": "1.6.1", - "turbo-windows-64": "1.6.1", - "turbo-windows-arm64": "1.6.1" + "turbo-darwin-64": "1.6.2", + "turbo-darwin-arm64": "1.6.2", + "turbo-linux-64": "1.6.2", + "turbo-linux-arm64": "1.6.2", + "turbo-windows-64": "1.6.2", + "turbo-windows-arm64": "1.6.2" } }, "node_modules/turbo-darwin-64": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/turbo-darwin-64/-/turbo-darwin-64-1.6.1.tgz", - "integrity": "sha512-xsItJ/hmnd6R8V60cCe0RAZQjO+En/LVXVkZhiw0Fyfxoo+iKcAA4sVeWkaL+cg5sQd5UWlWfD1EOKbHDjVb9Q==", + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/turbo-darwin-64/-/turbo-darwin-64-1.6.2.tgz", + "integrity": "sha512-qzqVdJZcVeu1d0mzeSxffgeOToSgM+Hl1y9yDiJQ4QQjiR/WGCzS9FtydLnk0ori8T1j/lxiiQz2sHjHYfLCTg==", "cpu": [ "x64" ], @@ -18880,9 +18880,9 @@ ] }, "node_modules/turbo-darwin-arm64": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/turbo-darwin-arm64/-/turbo-darwin-arm64-1.6.1.tgz", - "integrity": "sha512-wRfAJWCLYB29IGTx6sF6QvexK/89AbAgnfYA5yVcuUJT+xz2/zLeGcOODQBCnP4rB+vX5ipXLY0XjkLGl+z6fA==", + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/turbo-darwin-arm64/-/turbo-darwin-arm64-1.6.2.tgz", + "integrity": "sha512-EJvgWSjLwzJbAsqRuqeaYSdXNKwyiUfOfEOPerYCvwxC+ILiqpladjteMCzzF/z2OmIbPzuqZpyik30TfpIC9A==", "cpu": [ "arm64" ], @@ -18893,9 +18893,9 @@ ] }, "node_modules/turbo-linux-64": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/turbo-linux-64/-/turbo-linux-64-1.6.1.tgz", - "integrity": "sha512-NZ88muC3hHbWW/cBgl9DFFbyzDcFVvZHQBXKTwVA8l2yLOOvesX+aQ2Knr4Pxu9Kb0F3t6ABsOSf8SbI7CpJsg==", + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/turbo-linux-64/-/turbo-linux-64-1.6.2.tgz", + "integrity": "sha512-SJ5ThDApOyOavjvkYb6a1MKJbOvX4JEvSYxoZv+ZpN8g7A7x1SE9b7EnxFl26S4eyy0J4+r+YJBDBZ7sFkqRWQ==", "cpu": [ "x64" ], @@ -18906,9 +18906,9 @@ ] }, "node_modules/turbo-linux-arm64": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/turbo-linux-arm64/-/turbo-linux-arm64-1.6.1.tgz", - "integrity": "sha512-HDgx+0ozqMpoDBOSzWz43nYMDp/+giEz8+vmLOB6mTQU/9IlZQVwachzwkqLRsJyBUhYALBlWGcuRWO3KqXMmg==", + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/turbo-linux-arm64/-/turbo-linux-arm64-1.6.2.tgz", + "integrity": "sha512-0vHojVlvAelsYZo6p/xE9H9Dg8spNjXx+PO5RwP12ui8GuEqHb3Vomdb7AKDnWkIH7bpysNb9GbKEk7awQpGSg==", "cpu": [ "arm64" ], @@ -18919,9 +18919,9 @@ ] }, "node_modules/turbo-windows-64": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/turbo-windows-64/-/turbo-windows-64-1.6.1.tgz", - "integrity": "sha512-jnR0V0YBlFJKEoAeq0GQFLmZ1UNl6vh+RHTHX546+o5jKcE6nfp9oTOEwtR0PLutiuxxDDm6roAc+9mSfycffw==", + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/turbo-windows-64/-/turbo-windows-64-1.6.2.tgz", + "integrity": "sha512-j4mJmY5pEt7OaBXDM99BpDnJuSpiLVbVbUl4Ezp8w3gyIcGuxy3wAfLbQyZSLu8ke6R9SdmXIBAM2Y0MjS/Z1g==", "cpu": [ "x64" ], @@ -18932,9 +18932,9 @@ ] }, "node_modules/turbo-windows-arm64": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/turbo-windows-arm64/-/turbo-windows-arm64-1.6.1.tgz", - "integrity": "sha512-vOqw/iPgLjkwpni2vNFK9YO19lN9QZ8JG8v1unvL09/rnXyKpHygrYECj+efJptEVJKBG2xLIauJYmZ/2LV1Uw==", + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/turbo-windows-arm64/-/turbo-windows-arm64-1.6.2.tgz", + "integrity": "sha512-AlDYjxPU21YSefP3qMl/Zys5spglXtaBt2ZF5N2+OWcjiJZ/0mIIc69oFdzuAuiyoYkyWMdkoCSGwuljtl31vg==", "cpu": [ "arm64" ], @@ -34404,58 +34404,58 @@ } }, "turbo": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/turbo/-/turbo-1.6.1.tgz", - "integrity": "sha512-CkcJo17cbwfTzmxtxJo2AbbeVqaz1yQotBUqVwZDdcrVSNKci2nvw+JHJ3sy/z9YY9xOJmoRaZifbkja3UXUWA==", + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/turbo/-/turbo-1.6.2.tgz", + "integrity": "sha512-a6UM9HaAjM5ai+vxDFI/z0l4Bf6zWjf7wCf9Ip2iyd4XZkZZnhRtM6FpaUWzorjz9Dsqnfwu3nkWY3wPdH02IQ==", "dev": true, "requires": { - "turbo-darwin-64": "1.6.1", - "turbo-darwin-arm64": "1.6.1", - "turbo-linux-64": "1.6.1", - "turbo-linux-arm64": "1.6.1", - "turbo-windows-64": "1.6.1", - "turbo-windows-arm64": "1.6.1" + "turbo-darwin-64": "1.6.2", + "turbo-darwin-arm64": "1.6.2", + "turbo-linux-64": "1.6.2", + "turbo-linux-arm64": "1.6.2", + "turbo-windows-64": "1.6.2", + "turbo-windows-arm64": "1.6.2" } }, "turbo-darwin-64": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/turbo-darwin-64/-/turbo-darwin-64-1.6.1.tgz", - "integrity": "sha512-xsItJ/hmnd6R8V60cCe0RAZQjO+En/LVXVkZhiw0Fyfxoo+iKcAA4sVeWkaL+cg5sQd5UWlWfD1EOKbHDjVb9Q==", + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/turbo-darwin-64/-/turbo-darwin-64-1.6.2.tgz", + "integrity": "sha512-qzqVdJZcVeu1d0mzeSxffgeOToSgM+Hl1y9yDiJQ4QQjiR/WGCzS9FtydLnk0ori8T1j/lxiiQz2sHjHYfLCTg==", "dev": true, "optional": true }, "turbo-darwin-arm64": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/turbo-darwin-arm64/-/turbo-darwin-arm64-1.6.1.tgz", - "integrity": "sha512-wRfAJWCLYB29IGTx6sF6QvexK/89AbAgnfYA5yVcuUJT+xz2/zLeGcOODQBCnP4rB+vX5ipXLY0XjkLGl+z6fA==", + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/turbo-darwin-arm64/-/turbo-darwin-arm64-1.6.2.tgz", + "integrity": "sha512-EJvgWSjLwzJbAsqRuqeaYSdXNKwyiUfOfEOPerYCvwxC+ILiqpladjteMCzzF/z2OmIbPzuqZpyik30TfpIC9A==", "dev": true, "optional": true }, "turbo-linux-64": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/turbo-linux-64/-/turbo-linux-64-1.6.1.tgz", - "integrity": "sha512-NZ88muC3hHbWW/cBgl9DFFbyzDcFVvZHQBXKTwVA8l2yLOOvesX+aQ2Knr4Pxu9Kb0F3t6ABsOSf8SbI7CpJsg==", + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/turbo-linux-64/-/turbo-linux-64-1.6.2.tgz", + "integrity": "sha512-SJ5ThDApOyOavjvkYb6a1MKJbOvX4JEvSYxoZv+ZpN8g7A7x1SE9b7EnxFl26S4eyy0J4+r+YJBDBZ7sFkqRWQ==", "dev": true, "optional": true }, "turbo-linux-arm64": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/turbo-linux-arm64/-/turbo-linux-arm64-1.6.1.tgz", - "integrity": "sha512-HDgx+0ozqMpoDBOSzWz43nYMDp/+giEz8+vmLOB6mTQU/9IlZQVwachzwkqLRsJyBUhYALBlWGcuRWO3KqXMmg==", + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/turbo-linux-arm64/-/turbo-linux-arm64-1.6.2.tgz", + "integrity": "sha512-0vHojVlvAelsYZo6p/xE9H9Dg8spNjXx+PO5RwP12ui8GuEqHb3Vomdb7AKDnWkIH7bpysNb9GbKEk7awQpGSg==", "dev": true, "optional": true }, "turbo-windows-64": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/turbo-windows-64/-/turbo-windows-64-1.6.1.tgz", - "integrity": "sha512-jnR0V0YBlFJKEoAeq0GQFLmZ1UNl6vh+RHTHX546+o5jKcE6nfp9oTOEwtR0PLutiuxxDDm6roAc+9mSfycffw==", + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/turbo-windows-64/-/turbo-windows-64-1.6.2.tgz", + "integrity": "sha512-j4mJmY5pEt7OaBXDM99BpDnJuSpiLVbVbUl4Ezp8w3gyIcGuxy3wAfLbQyZSLu8ke6R9SdmXIBAM2Y0MjS/Z1g==", "dev": true, "optional": true }, "turbo-windows-arm64": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/turbo-windows-arm64/-/turbo-windows-arm64-1.6.1.tgz", - "integrity": "sha512-vOqw/iPgLjkwpni2vNFK9YO19lN9QZ8JG8v1unvL09/rnXyKpHygrYECj+efJptEVJKBG2xLIauJYmZ/2LV1Uw==", + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/turbo-windows-arm64/-/turbo-windows-arm64-1.6.2.tgz", + "integrity": "sha512-AlDYjxPU21YSefP3qMl/Zys5spglXtaBt2ZF5N2+OWcjiJZ/0mIIc69oFdzuAuiyoYkyWMdkoCSGwuljtl31vg==", "dev": true, "optional": true }, diff --git a/package.json b/package.json index c0c3080e..3ff6358d 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,7 @@ ], "scripts": { "build": "turbo run build --no-daemon", + "barrels": "turbo run barrels --no-daemon", "build-ladle": "turbo run build-ladle --no-daemon", "rollup": "turbo run rollup --no-daemon", "ssr-test": "turbo run ssr-test --no-daemon", @@ -54,7 +55,7 @@ "rollup-plugin-progress": "^1.1.2", "rollup-plugin-ts": "^3.0.2", "sass": "^1.55.0", - "turbo": "1.6.1", + "turbo": "1.6.2", "typescript": "^4.8.3" }, "engines": { diff --git a/packages/Schedulely/__tests__/dateAdapters/dateAdapter.spec.ts b/packages/Schedulely/__tests__/dateAdapters/dateAdapter.spec.ts index b2c10467..78d82022 100644 --- a/packages/Schedulely/__tests__/dateAdapters/dateAdapter.spec.ts +++ b/packages/Schedulely/__tests__/dateAdapters/dateAdapter.spec.ts @@ -9,7 +9,7 @@ import { getIsTodayTestCases, getMonthNameFromDateTestCases, getYearFromDateTestCases, -} from './dateAdapter.testHelper'; +} from '../testHelpers/dateAdapter.testHelper'; /** * Additional adapters should just be added to this array @@ -163,77 +163,6 @@ describe('Date Adapter', () => { ); }); - describe('getGridEndIndex', () => { - it.each<{ eventEnd: Date; endOfWeek: Date; expected: number }>([ - { - eventEnd: new Date(2022, 1, 11), - endOfWeek: new Date(2022, 1, 12), - expected: 7, - }, - { - // event ends after end of week - eventEnd: new Date(2022, 1, 13), - endOfWeek: new Date(2022, 1, 12), - expected: 8, - }, - { - eventEnd: new Date(2022, 1, 9), - endOfWeek: new Date(2022, 1, 12), - expected: 5, - }, - { - // event ends on Sunday - eventEnd: new Date(2021, 8, 26), - endOfWeek: new Date(2021, 9, 2), - expected: 2, - }, - { - // event that starts and ends on Sunday - eventEnd: new Date(2022, 0, 2), - endOfWeek: new Date(2022, 0, 8), - expected: 2, - }, - ])( - '$eventEnd with $endOfWeek returns $expected', - ({ eventEnd, endOfWeek, expected }) => { - const result = adapter.getGridEndIndex(eventEnd, endOfWeek); - expect(result).toBe(expected); - } - ); - }); - - describe('getGridStartIndex', () => { - it.each<{ eventStart: Date; startOfWeek: Date; expected: number }>([ - { - eventStart: new Date(2022, 1, 7), - startOfWeek: new Date(2022, 1, 6), - expected: 2, - }, - { - eventStart: new Date(2022, 1, 9), - startOfWeek: new Date(2022, 1, 6), - expected: 4, - }, - { - eventStart: new Date(2022, 3, 10), - startOfWeek: new Date(2022, 3, 10), - expected: 1, - }, - { - // event that starts and ends on Sunday - eventStart: new Date(2022, 0, 2), - startOfWeek: new Date(2022, 0, 2), - expected: 1, - }, - ])( - '$eventStart with $startOfWeek returns $expected', - ({ eventStart, startOfWeek, expected }) => { - const result = adapter.getGridStartIndex(eventStart, startOfWeek); - expect(result).toBe(expected); - } - ); - }); - describe('isEventInWeek', () => { it.each<{ message: string; diff --git a/packages/Schedulely/__tests__/hooks/useActions.spec.tsx b/packages/Schedulely/__tests__/hooks/useActions.spec.tsx index c3fbcfde..13c742fa 100644 --- a/packages/Schedulely/__tests__/hooks/useActions.spec.tsx +++ b/packages/Schedulely/__tests__/hooks/useActions.spec.tsx @@ -2,6 +2,7 @@ import { ActionProvider } from '@/providers'; import { InternalCalendarEvent } from '@/types'; import { ReactNode } from 'react'; import { act } from 'react-test-renderer'; +import { render } from '@testing-library/react'; import { renderHook } from '@testing-library/react-hooks'; import { useActions } from '@/hooks'; @@ -17,12 +18,10 @@ const testEvents: InternalCalendarEvent[] = [ ]; const onEventClickHandler = jest.fn((event: InternalCalendarEvent) => null); -const onMonthChangeClickHandler = jest.fn( +let onMonthChangeClickHandler = jest.fn( (firstOfMonth: Date, lastOfMonth: Date) => null ); -const onMoreEventClickHandler = jest.fn( - (event: InternalCalendarEvent[]) => null -); +let onMoreEventClickHandler = jest.fn((event: InternalCalendarEvent[]) => null); const wrapper = ({ children }: { children: ReactNode }) => ( { } = renderHook(() => useActions(), { wrapper }); describe('onEventClick', () => { - act(() => { - onEventClick(testEvents[0]); - }); + onEventClick(testEvents[0]); it('invokes correct function', () => { expect(onEventClickHandler).toHaveBeenCalled(); @@ -59,9 +56,8 @@ describe('useActions', () => { describe('onMonthChangeClick', () => { const monthStart = new Date(2000, 1, 1); const monthEnd = new Date(2000, 2, 1); - act(() => { - onMonthChangeClick(monthStart, monthEnd); - }); + + onMonthChangeClick(monthStart, monthEnd); it('invokes correct function', () => { expect(onMonthChangeClickHandler).toHaveBeenCalled(); @@ -74,15 +70,56 @@ describe('useActions', () => { }); describe('onMoreEventClick', () => { - act(() => { - onMoreEventClickHandler(testEvents); + describe('when defined', () => { + onMoreEventClick(testEvents); + + it('invokes correct function', () => { + expect(onMoreEventClickHandler).toHaveBeenCalled(); + }); + + it('passes correct args', () => + expect(onMoreEventClickHandler.mock.calls[0][0]).toEqual(testEvents)); }); + }); - it('invokes correct function', () => { - expect(onMoreEventClickHandler).toHaveBeenCalled(); + it('throws when called outside of provider', () => { + const ExceptionWrapper = () => { + expect(useActions).toThrowError(/must be used within/); + return <>; + }; + render(); + }); + + describe('if Action undefined', () => { + const emptyWrapper = ({ children }: { children: ReactNode }) => ( + {children} + ); + + const { + result: { + current: { onEventClick, onMonthChangeClick, onMoreEventClick }, + }, + } = renderHook(() => useActions(), { wrapper: emptyWrapper }); + + beforeEach(() => { + onMoreEventClickHandler.mockClear(); + onEventClickHandler.mockClear(); + onMonthChangeClickHandler.mockClear(); }); - it('passes correct args', () => - expect(onMoreEventClickHandler.mock.calls[0][0]).toEqual(testEvents)); + it('does not invoke onMoreEventClick if not defined', () => { + onMoreEventClick(testEvents); + expect(onMoreEventClickHandler).not.toHaveBeenCalled(); + }); + + it('does not invoke onEventClick if not defined', () => { + onEventClick(testEvents[0]); + expect(onEventClickHandler).not.toHaveBeenCalled(); + }); + + it('does not invoke onMonthChangeClick if not defined', () => { + onMonthChangeClick(new Date(), new Date()); + expect(onMonthChangeClickHandler).not.toHaveBeenCalled(); + }); }); }); diff --git a/packages/Schedulely/__tests__/hooks/useBreakpoint.spec.tsx b/packages/Schedulely/__tests__/hooks/useBreakpoint.spec.tsx new file mode 100644 index 00000000..e88692f3 --- /dev/null +++ b/packages/Schedulely/__tests__/hooks/useBreakpoint.spec.tsx @@ -0,0 +1,12 @@ +import { render } from '@testing-library/react'; +import { useBreakpoint } from '@/hooks'; + +describe('useBreakpoint', () => { + it('throws when called outside of provider', () => { + const ExceptionWrapper = () => { + expect(useBreakpoint).toThrowError(/must be used within/); + return <>; + }; + render(); + }); +}); diff --git a/packages/Schedulely/__tests__/hooks/useCalendar.spec.tsx b/packages/Schedulely/__tests__/hooks/useCalendar.spec.tsx new file mode 100644 index 00000000..15a93fcf --- /dev/null +++ b/packages/Schedulely/__tests__/hooks/useCalendar.spec.tsx @@ -0,0 +1,12 @@ +import { render } from '@testing-library/react'; +import { useCalendar } from '@/hooks'; + +describe('useCalendar', () => { + it('throws when called outside of provider', () => { + const ExceptionWrapper = () => { + expect(useCalendar).toThrowError(/must be used within/); + return <>; + }; + render(); + }); +}); diff --git a/packages/Schedulely/__tests__/hooks/useComponents.spec.tsx b/packages/Schedulely/__tests__/hooks/useComponents.spec.tsx new file mode 100644 index 00000000..6e38c97b --- /dev/null +++ b/packages/Schedulely/__tests__/hooks/useComponents.spec.tsx @@ -0,0 +1,12 @@ +import { render } from '@testing-library/react'; +import { useComponents } from '@/hooks'; + +describe('useComponents', () => { + it('throws when called outside of provider', () => { + const ExceptionWrapper = () => { + expect(useComponents).toThrowError(/must be used within/); + return <>; + }; + render(); + }); +}); diff --git a/packages/Schedulely/__tests__/hooks/useEventHighlight.spec.tsx b/packages/Schedulely/__tests__/hooks/useEventHighlight.spec.tsx index 0ef70a9e..516b8daf 100644 --- a/packages/Schedulely/__tests__/hooks/useEventHighlight.spec.tsx +++ b/packages/Schedulely/__tests__/hooks/useEventHighlight.spec.tsx @@ -1,6 +1,7 @@ import { HighlightProvider } from '@/providers'; import { ReactNode } from 'react'; import { act } from 'react-test-renderer'; +import { render } from '@testing-library/react'; import { renderHook } from '@testing-library/react-hooks'; import { useEventHighlight } from '@/hooks'; import Chance from 'chance'; @@ -38,4 +39,12 @@ describe('useEventHighlight', () => { act(() => result.current.setHighlight('new-value')); expect(result.current.isHighlighted('new-value')).toBeTruthy(); }); + + it('throws when called outside of provider', () => { + const ExceptionWrapper = () => { + expect(useEventHighlight).toThrowError(/must be used within/); + return <>; + }; + render(); + }); }); diff --git a/packages/Schedulely/__tests__/hooks/useEventIntersection.spec.tsx b/packages/Schedulely/__tests__/hooks/useEventIntersection.spec.tsx new file mode 100644 index 00000000..0cc21e45 --- /dev/null +++ b/packages/Schedulely/__tests__/hooks/useEventIntersection.spec.tsx @@ -0,0 +1,12 @@ +import { render } from '@testing-library/react'; +import { useEventIntersection } from '@/hooks'; + +describe('useEventIntersection', () => { + it('throws when called outside of provider', () => { + const ExceptionWrapper = () => { + expect(useEventIntersection).toThrowError(/must be used within/); + return <>; + }; + render(); + }); +}); diff --git a/packages/Schedulely/__tests__/hooks/useKeyboardControls.spec.tsx b/packages/Schedulely/__tests__/hooks/useKeyboardControls.spec.tsx new file mode 100644 index 00000000..9942bf8f --- /dev/null +++ b/packages/Schedulely/__tests__/hooks/useKeyboardControls.spec.tsx @@ -0,0 +1,48 @@ +import { RenderResult, fireEvent, render } from '@testing-library/react'; +import { useKeyboardControls } from '@/hooks/useKeyboardControls'; + +const mockOnNextMonth = jest.fn(); +const mockOnPrevMonth = jest.fn(); +const mockOnNextYear = jest.fn(); +const mockOnPrevYear = jest.fn(); +jest.mock('@/hooks/useCalendar', () => ({ + useCalendar: jest.fn(() => ({ + onNextMonth: mockOnNextMonth, + onPrevMonth: mockOnPrevMonth, + onNextYear: mockOnNextYear, + onPrevYear: mockOnPrevYear, + })), +})); + +const TestWrapper = () => { + useKeyboardControls(); + return
; +}; + +describe('useKeyboardControls', () => { + let testObject: RenderResult; + + beforeEach(() => { + testObject = render(); + }); + + it('ArrowUp calls onNextYear', () => { + fireEvent.keyDown(testObject.container, { key: 'ArrowUp' }); + expect(mockOnNextYear).toHaveBeenCalledTimes(1); + }); + + it('ArrowRight calls onNextMonth', () => { + fireEvent.keyDown(testObject.container, { key: 'ArrowRight' }); + expect(mockOnNextMonth).toHaveBeenCalledTimes(1); + }); + + it('ArrowLeft calls onPrevMonth', () => { + fireEvent.keyDown(testObject.container, { key: 'ArrowLeft' }); + expect(mockOnPrevMonth).toHaveBeenCalledTimes(1); + }); + + it('ArrowDown calls onPrevYear', () => { + fireEvent.keyDown(testObject.container, { key: 'ArrowDown' }); + expect(mockOnPrevYear).toHaveBeenCalledTimes(1); + }); +}); diff --git a/packages/Schedulely/__tests__/layouts/DayOfWeekLayout.spec.tsx b/packages/Schedulely/__tests__/layouts/DayOfWeekLayout.spec.tsx new file mode 100644 index 00000000..390ff2eb --- /dev/null +++ b/packages/Schedulely/__tests__/layouts/DayOfWeekLayout.spec.tsx @@ -0,0 +1,27 @@ +import { Chance } from 'chance'; +import { DayOfWeekLayout } from '@/layouts'; +import { RenderResult, render } from '@testing-library/react'; + +// randomize day names for the hell of it +const chance = Chance(); +const mockDaysOfTheWeek = chance.unique(() => chance.string(), 7); + +jest.mock('@/hooks', () => ({ + useCalendar: jest.fn(() => ({ + daysOfWeek: mockDaysOfTheWeek, + })), +})); + +describe('DayOfWeekLayout', () => { + let testObject: RenderResult; + + beforeEach(() => { + testObject = render(); + }); + + describe('headers are all rendered', () => { + test.each(mockDaysOfTheWeek)('%s is rendered', (value) => + expect(testObject.getByText(value)).toBeTruthy() + ); + }); +}); diff --git a/packages/Schedulely/__tests__/layouts/EventWeekLayout.spec.tsx b/packages/Schedulely/__tests__/layouts/EventWeekLayout.spec.tsx new file mode 100644 index 00000000..37d2451a --- /dev/null +++ b/packages/Schedulely/__tests__/layouts/EventWeekLayout.spec.tsx @@ -0,0 +1,216 @@ +import { DefaultEvent } from '@/components'; +import { EventWeekLayout, getGridEndIndex, getGridStartIndex } from '@/layouts'; +import { InternalCalendarEvent } from '@/types'; +import { RenderResult, fireEvent, render } from '@testing-library/react'; + +// Oct 2-8 2022 is the test week +const daysInWeek = [ + new Date(2022, 9, 2), + new Date(2022, 9, 3), + new Date(2022, 9, 4), + new Date(2022, 9, 5), + new Date(2022, 9, 6), + new Date(2022, 9, 7), + new Date(2022, 9, 8), +]; + +let events: InternalCalendarEvent[] = [ + { + id: 'event-1', + color: 'red', + start: new Date(2022, 9, 2), + end: new Date(2022, 9, 2), + summary: 'event-1', + visible: true, + }, + { + id: 'event-2', + color: 'red', + start: new Date(2022, 9, 2), + end: new Date(2022, 9, 2), + summary: 'event-2', + visible: true, + }, + { + id: 'event-3', + color: 'red', + start: new Date(2022, 9, 2), + end: new Date(2022, 9, 2), + summary: 'event-3', + visible: true, + }, +]; + +let mockSetHighlight = jest.fn((eventId: string) => {}); +let mockClearHighlight = jest.fn(() => {}); +let mockIsHighlighted = jest.fn((eventId: string) => false); + +let mockEventOnClickHandler = jest.fn(() => {}); + +let mockSetParentContainerRef = jest.fn((eventId: string) => {}); +let mockSetRefFromKey = jest.fn((eventId: string) => {}); +let mockIsEventVisible = jest.fn((eventId: string) => true); + +jest.mock('@/hooks', () => ({ + useComponents: jest.fn(() => ({ + eventComponent: DefaultEvent, + })), + useEventHighlight: jest.fn(() => ({ + setHighlight: mockSetHighlight, + clearHighlight: mockClearHighlight, + isHighlighted: mockIsHighlighted, + })), + useActions: jest.fn(() => ({ + onEventClick: mockEventOnClickHandler, + })), + useEventIntersection: jest.fn(() => ({ + setParentContainerRef: mockSetParentContainerRef, + setRefFromKey: mockSetRefFromKey, + isEventVisible: mockIsEventVisible, + })), +})); + +describe('EventWeekLayout', () => { + let testObject: RenderResult; + + beforeEach(() => { + mockSetParentContainerRef.mockClear(); + testObject = render( + + ); + }); + + afterEach(() => { + mockIsEventVisible.mockClear(); + mockSetRefFromKey.mockClear(); + }); + + describe.each(events.map((x) => x.summary))('event %s', (value) => { + let eventDomObject: HTMLElement; + + beforeEach(() => { + eventDomObject = testObject.getByText(value); + }); + + afterEach(() => { + mockSetHighlight.mockClear(); + mockClearHighlight.mockClear(); + mockEventOnClickHandler.mockClear(); + }); + + it('is in document', () => expect(eventDomObject).toBeTruthy()); + + it('calls setHighlight onMouseOver', () => { + fireEvent.mouseOver(eventDomObject); + expect(mockSetHighlight).toHaveBeenCalledTimes(1); + }); + + it('passes eventID to set highlight onMouseOver', () => { + fireEvent.mouseOver(eventDomObject); + expect(mockSetHighlight).toHaveBeenLastCalledWith(value); + }); + + it('is cleared onMouseLeave', () => { + fireEvent.mouseLeave(eventDomObject); + expect(mockClearHighlight).toHaveBeenCalledTimes(1); + }); + + it('event onClick handler is called when clicked', () => { + fireEvent.click(eventDomObject.parentElement!); + expect(mockEventOnClickHandler).toHaveBeenCalledTimes(1); + }); + }); + + // we can't really test this well in jest without a massive setup. Maybe later. + describe('useEventIntersection', () => { + it('setParentContainerRef is called on the parent', () => + expect(mockSetParentContainerRef).toHaveBeenCalledTimes(1)); + + it('isEventVisible is called', () => + expect(mockIsEventVisible).toHaveBeenCalledTimes(events.length)); + + it('isEventVisible is called with each eventId', () => + expect(mockIsEventVisible.mock.calls.flat().sort()).toEqual( + events.map((x) => x.id).sort() + )); + + it('setRefFromKey is called', () => + expect(mockSetRefFromKey).toHaveBeenCalledTimes(events.length)); + + it('setRefFromKey is called with each eventId', () => + expect(mockSetRefFromKey.mock.calls.flat().sort()).toEqual( + events.map((x) => x.id).sort() + )); + }); + + describe('getGridEndIndex', () => { + it.each<{ eventEnd: Date; endOfWeek: Date; expected: number }>([ + { + eventEnd: new Date(2022, 1, 11), + endOfWeek: new Date(2022, 1, 12), + expected: 7, + }, + { + // event ends after end of week + eventEnd: new Date(2022, 1, 13), + endOfWeek: new Date(2022, 1, 12), + expected: 8, + }, + { + eventEnd: new Date(2022, 1, 9), + endOfWeek: new Date(2022, 1, 12), + expected: 5, + }, + { + // event ends on Sunday + eventEnd: new Date(2021, 8, 26), + endOfWeek: new Date(2021, 9, 2), + expected: 2, + }, + { + // event that starts and ends on Sunday + eventEnd: new Date(2022, 0, 2), + endOfWeek: new Date(2022, 0, 8), + expected: 2, + }, + ])( + '$eventEnd with $endOfWeek returns $expected', + ({ eventEnd, endOfWeek, expected }) => { + const result = getGridEndIndex(eventEnd, endOfWeek); + expect(result).toBe(expected); + } + ); + }); + + describe('getGridStartIndex', () => { + it.each<{ eventStart: Date; startOfWeek: Date; expected: number }>([ + { + eventStart: new Date(2022, 1, 7), + startOfWeek: new Date(2022, 1, 6), + expected: 2, + }, + { + eventStart: new Date(2022, 1, 9), + startOfWeek: new Date(2022, 1, 6), + expected: 4, + }, + { + eventStart: new Date(2022, 3, 10), + startOfWeek: new Date(2022, 3, 10), + expected: 1, + }, + { + // event that starts and ends on Sunday + eventStart: new Date(2022, 0, 2), + startOfWeek: new Date(2022, 0, 2), + expected: 1, + }, + ])( + '$eventStart with $startOfWeek returns $expected', + ({ eventStart, startOfWeek, expected }) => { + const result = getGridStartIndex(eventStart, startOfWeek); + expect(result).toBe(expected); + } + ); + }); +}); diff --git a/packages/Schedulely/__tests__/layouts/HeaderLayout.spec.tsx b/packages/Schedulely/__tests__/layouts/HeaderLayout.spec.tsx new file mode 100644 index 00000000..7cdeea9b --- /dev/null +++ b/packages/Schedulely/__tests__/layouts/HeaderLayout.spec.tsx @@ -0,0 +1,91 @@ +import { Chance } from 'chance'; +import { DefaultHeader } from '@/components'; +import { HeaderComponent } from '@/types'; +import { HeaderLayout } from '@/layouts'; +import { RenderResult, fireEvent, render } from '@testing-library/react'; + +let mockHeaderComponent: HeaderComponent = DefaultHeader; +const mockCurrentMonth = Chance().month(); +const mockCurrentYear = Chance().year(); +const mockIsCurrentMonth = Chance().bool(); +const mockOnNextMonthClick = jest.fn(); +const mockOnNextYearHandler = jest.fn(); +const mockOnPrevMonthHandler = jest.fn(); +const mockOnPrevYearHandler = jest.fn(); + +jest.mock('@/hooks', () => ({ + useComponents: jest.fn(() => ({ + headerComponent: mockHeaderComponent, + })), + useCalendar: jest.fn(() => ({ + currentMonth: mockCurrentMonth, + currentYear: mockCurrentYear, + isCurrentMonth: mockIsCurrentMonth, + onNextMonth: mockOnNextMonthClick, + onNextYear: mockOnNextYearHandler, + onPrevYear: mockOnPrevYearHandler, + onPrevMonth: mockOnPrevMonthHandler, + })), + useBreakpoint: jest.fn(() => ({ + breakpoint: 'small', + })), +})); + +describe('HeaderLayout', () => { + describe('with DefaultHeader component', () => { + let testObject: RenderResult; + + beforeEach(() => { + testObject = render(); + }); + + it('passes currentMonth value', () => + expect( + testObject.getByText(mockCurrentMonth, { exact: false }) + ).toBeTruthy()); + + it('passes currentYear value', () => + expect( + testObject.getByText(mockCurrentYear, { exact: false }) + ).toBeTruthy()); + + it('passes isCurrentMonth', () => { + const result = expect(testObject.queryByRole('alert')); + mockIsCurrentMonth ? result.toBeTruthy() : result.toBeFalsy(); + }); + + it('passes onNextMonthHandler', () => { + fireEvent.click(testObject.getByTitle('Next Month')); + expect(mockOnNextMonthClick).toHaveBeenCalledTimes(1); + }); + + it('passes onNextYearHandler', () => { + fireEvent.click(testObject.getByTitle('Next Year')); + expect(mockOnNextYearHandler).toHaveBeenCalledTimes(1); + }); + + it('passes onPrevMonthHandler', () => { + fireEvent.click(testObject.getByTitle('Previous Month')); + expect(mockOnPrevMonthHandler).toHaveBeenCalledTimes(1); + }); + + it('passes onPrevYearHandler', () => { + fireEvent.click(testObject.getByTitle('Previous Year')); + expect(mockOnPrevYearHandler).toHaveBeenCalledTimes(1); + }); + }); + + describe('with custom header component', () => { + let testObject: RenderResult; + let mockHeaderText: string; + + beforeEach(() => { + mockHeaderText = Chance().string(); + mockHeaderComponent = () =>
{mockHeaderText}
; + testObject = render(); + }); + + it('uses custom header component', () => + expect(testObject.getByText(mockHeaderText)).toBeTruthy()); + }); +}); diff --git a/packages/Schedulely/__tests__/layouts/MonthLayout.spec.tsx b/packages/Schedulely/__tests__/layouts/MonthLayout.spec.tsx new file mode 100644 index 00000000..893fdd82 --- /dev/null +++ b/packages/Schedulely/__tests__/layouts/MonthLayout.spec.tsx @@ -0,0 +1,211 @@ +import { EventLayoutProps, MonthLayout, WeekLayoutProps } from '@/layouts'; +import { InternalCalendarEvent, InternalEventWeek } from '@/types'; +import { ReactNode } from 'react'; +import { RenderResult, render } from '@testing-library/react'; + +const mockCalendarWithEvents = [ + { + weekStart: new Date(2022, 8, 25), + weekEnd: new Date(2022, 9, 1), + daysInWeek: [ + new Date(2022, 8, 25), + new Date(2022, 8, 26), + new Date(2022, 8, 27), + new Date(2022, 8, 28), + new Date(2022, 8, 29), + new Date(2022, 8, 30), + new Date(2022, 9, 1), + ], + events: [] as InternalCalendarEvent[], + eventsOnDays: {}, + }, + { + weekStart: new Date(2022, 9, 2), + weekEnd: new Date(2022, 9, 8), + daysInWeek: [ + new Date(2022, 9, 2), + new Date(2022, 9, 3), + new Date(2022, 9, 4), + new Date(2022, 9, 5), + new Date(2022, 9, 6), + new Date(2022, 9, 7), + new Date(2022, 9, 8), + ], + events: [] as InternalCalendarEvent[], + eventsOnDays: {}, + }, + { + weekStart: new Date(2022, 9, 9), + weekEnd: new Date(2022, 9, 15), + daysInWeek: [ + new Date(2022, 9, 9), + new Date(2022, 9, 10), + new Date(2022, 9, 11), + new Date(2022, 9, 12), + new Date(2022, 9, 13), + new Date(2022, 9, 14), + new Date(2022, 9, 15), + ], + events: [] as InternalCalendarEvent[], + eventsOnDays: {}, + }, + { + weekStart: new Date(2022, 9, 16), + weekEnd: new Date(2022, 9, 22), + daysInWeek: [ + new Date(2022, 9, 16), + new Date(2022, 9, 17), + new Date(2022, 9, 18), + new Date(2022, 9, 19), + new Date(2022, 9, 20), + new Date(2022, 9, 21), + new Date(2022, 9, 22), + ], + events: [] as InternalCalendarEvent[], + eventsOnDays: {}, + }, + { + weekStart: new Date(2022, 9, 23), + weekEnd: new Date(2022, 9, 29), + daysInWeek: [ + new Date(2022, 9, 23), + new Date(2022, 9, 24), + new Date(2022, 9, 25), + new Date(2022, 9, 26), + new Date(2022, 9, 27), + new Date(2022, 9, 28), + new Date(2022, 9, 29), + ], + events: [] as InternalCalendarEvent[], + eventsOnDays: {}, + }, + { + weekStart: new Date(2022, 9, 30), + weekEnd: new Date(2022, 10, 5), + daysInWeek: [ + new Date(2022, 9, 30), + new Date(2022, 9, 31), + new Date(2022, 1, 1), + new Date(2022, 1, 2), + new Date(2022, 1, 3), + new Date(2022, 1, 4), + new Date(2022, 1, 5), + ], + events: [] as InternalCalendarEvent[], + eventsOnDays: {}, + }, +] as InternalEventWeek[]; + +const mockUseKeyboardControls = jest.fn(() => null); + +jest.mock('@/hooks', () => ({ + useCalendar: jest.fn(() => ({ + calendarWithEvents: mockCalendarWithEvents, + })), + useKeyboardControls: jest.fn(() => mockUseKeyboardControls()), +})); + +const mockEventIntersectionProviderPropsCheck = jest.fn(); +jest.mock('@/providers', () => ({ + EventIntersectionProvider: jest.fn( + ({ + children, + events, + }: { + children: ReactNode; + events: InternalCalendarEvent[]; + }) => { + mockEventIntersectionProviderPropsCheck(events); + return
{children}
; + } + ), + HighlightProvider: jest.fn(({ children }: { children: ReactNode }) => ( +
{children}
+ )), +})); + +const mockEventWeekPropsCheck = jest.fn(); +jest.mock('@/layouts/eventWeekLayout', () => ({ + EventWeekLayout: jest.fn(({ events, daysInweek }: EventLayoutProps) => { + mockEventWeekPropsCheck(events, daysInweek); + return
; + }), +})); + +const mockWeekLayoutPropsCheck = jest.fn(); +jest.mock('@/layouts/weekLayout', () => ({ + WeekLayout: jest.fn(({ dates }: WeekLayoutProps) => { + mockWeekLayoutPropsCheck(dates); + return
; + }), +})); + +describe('MonthLayout', () => { + let testObject: RenderResult; + + beforeEach(() => { + testObject = render(); + }); + + afterEach(() => { + mockUseKeyboardControls.mockClear(); + }); + + describe.each(mockCalendarWithEvents.map((row, i) => ({ ...row, index: i })))( + 'Week $index', + (week) => { + describe('EventWeekLayout', () => { + it('receives array of days', () => { + expect(mockEventWeekPropsCheck.mock.calls[week.index][1]).toEqual( + mockCalendarWithEvents[week.index].daysInWeek + ); + }); + + it('receives array of events', () => { + expect(mockEventWeekPropsCheck.mock.calls[week.index][0]).toEqual( + mockCalendarWithEvents[week.index].events + ); + }); + }); + + describe('WeekLayout', () => { + it('receives array of days', () => { + expect(mockWeekLayoutPropsCheck.mock.calls[week.index][0]).toEqual( + mockCalendarWithEvents[week.index].daysInWeek + ); + }); + }); + + describe('EventIntersectionProvider', () => { + it('receives array of days', () => { + expect( + mockEventIntersectionProviderPropsCheck.mock.calls[week.index][0] + ).toEqual(mockCalendarWithEvents[week.index].events); + }); + }); + } + ); + + it('initializes keyboard controls', () => + expect(mockUseKeyboardControls).toHaveBeenCalledTimes(1)); + + it('renders one highlight provider', () => + expect(testObject.getAllByTestId('highlight-provider-mock').length).toEqual( + 1 + )); + + it('renders the correct number of intersection providers', () => + expect( + testObject.queryAllByTestId('intersection-provider-mock').length + ).toEqual(mockCalendarWithEvents.length)); + + it('renders the correct number of EventWeekLayout', () => + expect( + testObject.queryAllByTestId('event-week-layout-mock').length + ).toEqual(mockCalendarWithEvents.length)); + + it('renders the correct number of WeekLayout', () => + expect(testObject.queryAllByTestId('week-layout-mock').length).toEqual( + mockCalendarWithEvents.length + )); +}); diff --git a/packages/Schedulely/__tests__/layouts/WeekLayout.spec.tsx b/packages/Schedulely/__tests__/layouts/WeekLayout.spec.tsx new file mode 100644 index 00000000..7adadd30 --- /dev/null +++ b/packages/Schedulely/__tests__/layouts/WeekLayout.spec.tsx @@ -0,0 +1,133 @@ +import { DayComponentProps, InternalCalendarEvent } from '@/types'; +import { RenderResult, render } from '@testing-library/react'; +import { WeekLayout } from '@/layouts'; + +// Oct 2-8 2022 is the test week +const dates = [ + new Date(2022, 9, 2), + new Date(2022, 9, 3), + new Date(2022, 9, 4), + new Date(2022, 9, 5), + new Date(2022, 9, 6), + new Date(2022, 9, 7), + new Date(2022, 9, 8), +]; + +let mockOnMoreEventClick = jest.fn((events: InternalCalendarEvent[]) => {}); +let mockGetEventsOnDate = jest.fn( + (date: Date) => + [ + { + id: 'event-1', + color: 'red', + start: new Date(2022, 9, 2), + end: new Date(2022, 9, 2), + summary: 'event-1', + visible: true, + }, + { + id: 'event-1', + color: 'red', + start: new Date(2022, 9, 2), + end: new Date(2022, 9, 8), + summary: 'event-1', + visible: false, + }, + ] as InternalCalendarEvent[] +); +let mockIsDateToday = jest.fn((date: Date) => true); +let mockIsSameMonth = jest.fn((date: Date, date2: Date) => true); +let mockGetDayNumber = jest.fn((date: Date) => date.getDate()); +let mockCurrentDate = jest.fn(() => Date); + +const mockDayComponentPropsCheck = jest.fn(); +jest.mock('@/hooks', () => ({ + useComponents: jest.fn(() => ({ + dayComponent: jest.fn((props: DayComponentProps) => { + mockDayComponentPropsCheck(props); + return
; + }), + })), + useCalendar: jest.fn(() => ({ + dateAdapter: { + isDateToday: mockIsDateToday, + isSameMonth: mockIsSameMonth, + getDayNumber: mockGetDayNumber, + }, + currentDate: mockCurrentDate, + })), + useActions: jest.fn(() => ({ + onMoreEventClick: mockOnMoreEventClick, + })), + useEventIntersection: jest.fn(() => ({ + getEventsOnDate: mockGetEventsOnDate, + })), +})); + +describe('WeekLayout', () => { + let testObject: RenderResult; + + beforeEach(() => { + testObject = render(); + }); + + afterEach(() => { + mockGetEventsOnDate.mockClear(); + mockIsDateToday.mockClear(); + mockGetDayNumber.mockClear(); + mockIsSameMonth.mockClear(); + }); + + it('calls isDateToday', () => + expect(mockIsDateToday).toHaveBeenCalledTimes(dates.length)); + + it('isDateToday is called with each date', () => + expect( + mockIsDateToday.mock.calls + .flat() + .filter((x, i, a) => a.indexOf(x) == i) + .sort() + ).toEqual(dates.map((x) => x).sort())); + + it('calls getDayNumber', () => + expect(mockGetDayNumber).toHaveBeenCalledTimes(dates.length)); + + it('getDayNumber is called with each date', () => + expect( + mockGetDayNumber.mock.calls + .flat() + .filter((x, i, a) => a.indexOf(x) == i) + .sort() + ).toEqual(dates.map((x) => x).sort())); + + it('calls isSameMonth', () => + expect(mockIsSameMonth).toHaveBeenCalledTimes(dates.length)); + + it('isSameMonth is called with each date', () => { + expect( + mockIsSameMonth.mock.calls + .map((x) => x[0]) + .filter((x, i, a) => a.indexOf(x) == i) + .sort() + ).toEqual(dates.map((x) => x).sort()); + }); + + it('calls getEventsOnDate', () => + expect(mockGetEventsOnDate).toHaveBeenCalledTimes(dates.length)); + + describe.each(dates.map((x, index) => ({ value: x, index })))( + 'date container for %s', + ({ value, index }) => { + it('is rendered', () => + expect( + testObject.getByTestId(value.getDate().toString()) + ).toBeTruthy()); + + it('passes OnMoreEventClickHandler', () => { + expect(mockOnMoreEventClick).toEqual( + mockDayComponentPropsCheck.mock.calls[index][0].onClick + ); + }); + } + ); +}); diff --git a/packages/Schedulely/__tests__/testHelpers/component.testHelper.ts b/packages/Schedulely/__tests__/testHelpers/component.testHelper.ts new file mode 100644 index 00000000..a575718d --- /dev/null +++ b/packages/Schedulely/__tests__/testHelpers/component.testHelper.ts @@ -0,0 +1,33 @@ +import { CalendarContextState, SchedulelyComponents } from '@/types'; +import { DefaultDay, DefaultEvent, DefaultHeader } from '@/components'; +import { createDefaultAdapter } from '@/dateAdapters'; + +export const getCalendarProviderProps = ( + overrides: Partial | null +) => { + const hook: CalendarContextState = { + currentDate: new Date(), + currentMonth: '', + currentYear: 1, + isCurrentMonth: false, + dateAdapter: createDefaultAdapter(), + daysOfWeek: [], + onNextMonth: () => null, + onPrevMonth: () => null, + onNextYear: () => null, + onPrevYear: () => null, + calendarWithEvents: [], + }; + return { hook, ...overrides } as CalendarContextState; +}; + +export const getComponentProviderProps = ( + overrides: Partial +) => { + const hook: SchedulelyComponents = { + dayComponent: DefaultDay, + headerComponent: DefaultHeader, + eventComponent: DefaultEvent, + }; + return { hook, ...overrides }; +}; diff --git a/packages/Schedulely/__tests__/dateAdapters/dateAdapter.testHelper.ts b/packages/Schedulely/__tests__/testHelpers/dateAdapter.testHelper.ts similarity index 100% rename from packages/Schedulely/__tests__/dateAdapters/dateAdapter.testHelper.ts rename to packages/Schedulely/__tests__/testHelpers/dateAdapter.testHelper.ts diff --git a/packages/Schedulely/jest.config.js b/packages/Schedulely/jest.config.js index 2c4edc82..6800ea3d 100644 --- a/packages/Schedulely/jest.config.js +++ b/packages/Schedulely/jest.config.js @@ -3,7 +3,6 @@ module.exports = { moduleNameMapper: { '@/(.*)$': '/src/$1', - // '^.+\\.scss$': 'jest-transform-scss', }, testEnvironment: 'jsdom', transform: { diff --git a/packages/Schedulely/src/dateAdapters/date.ts b/packages/Schedulely/src/dateAdapters/date.ts index 6d6f1374..59ccc859 100644 --- a/packages/Schedulely/src/dateAdapters/date.ts +++ b/packages/Schedulely/src/dateAdapters/date.ts @@ -103,15 +103,6 @@ export const createDefaultAdapter = (locale = 'en'): DateTimeAdapter => { const addMonthsToDate = (date: Date, amount: number) => new Date(date.getFullYear(), date.getMonth() + amount, 1); - const getGridStartIndex = (eventDate: Date, startOfWeek: Date) => - eventDate <= startOfWeek ? 1 : eventDate.getDay() + 1; //add one because css-grid isn't zero-index'd - - const getGridEndIndex = (eventEndDate: Date, endOfWeek: Date) => { - if (eventEndDate > endOfWeek) return 8; - const end = eventEndDate.getDay() + 2; // add two because css-grid isn't zero index'd, and day of week is zero-index'd - return end; - }; - const isEventInWeek = ( eventStartDate: Date, eventEndDate: Date, @@ -148,8 +139,6 @@ export const createDefaultAdapter = (locale = 'en'): DateTimeAdapter => { isSameMonth, isDateToday, addMonthsToDate, - getGridEndIndex, - getGridStartIndex, isEventInWeek, convertIsoToDate, isCurrentMonth, diff --git a/packages/Schedulely/src/hooks/useActions.tsx b/packages/Schedulely/src/hooks/useActions.tsx index 01c3568d..26b19784 100644 --- a/packages/Schedulely/src/hooks/useActions.tsx +++ b/packages/Schedulely/src/hooks/useActions.tsx @@ -1,4 +1,4 @@ -import { ActionContext } from '@/providers'; +import { ActionContext } from '@/providers/ActionProvider'; import { useContext } from 'react'; export const useActions = () => { diff --git a/packages/Schedulely/src/layouts/dayOfWeekLayout/DayOfWeekLayout.tsx b/packages/Schedulely/src/layouts/dayOfWeekLayout/DayOfWeekLayout.tsx index 7f6b596d..307c2741 100644 --- a/packages/Schedulely/src/layouts/dayOfWeekLayout/DayOfWeekLayout.tsx +++ b/packages/Schedulely/src/layouts/dayOfWeekLayout/DayOfWeekLayout.tsx @@ -1,17 +1,16 @@ -import { useBreakpoint, useCalendar } from '@/hooks'; +import { useCalendar } from '@/hooks'; /** * This component controls the layout of the names of the days of the week at the top of the calendar * @returns DayOfWeekLayout Component */ export const DayOfWeekLayout = () => { - const { getDaysOfWeek } = useCalendar(); - const { breakpoint } = useBreakpoint(); + const { daysOfWeek } = useCalendar(); return (
{/* We cheat a bit here and use the index as part of the key. This is because short day names can be identical. */} - {getDaysOfWeek(breakpoint).map((dayOfWeek, idx) => ( + {daysOfWeek.map((dayOfWeek, idx) => (
{/* wrap with extra div so we can control borders without touching the DayOfWeek component */}
{dayOfWeek}
diff --git a/packages/Schedulely/src/layouts/eventWeekLayout/EventWeekLayout.tsx b/packages/Schedulely/src/layouts/eventWeekLayout/EventWeekLayout.tsx index 68881223..3928371c 100644 --- a/packages/Schedulely/src/layouts/eventWeekLayout/EventWeekLayout.tsx +++ b/packages/Schedulely/src/layouts/eventWeekLayout/EventWeekLayout.tsx @@ -1,25 +1,30 @@ import { InternalCalendarEvent } from '@/types/InternalCalendarEvent'; import { useActions, - useCalendar, useComponents, useEventHighlight, useEventIntersection, } from '@/hooks'; -interface EventLayoutProps { +export interface EventLayoutProps { events: InternalCalendarEvent[]; daysInweek: Date[]; } +export const getGridStartIndex = (eventDate: Date, startOfWeek: Date) => + eventDate <= startOfWeek ? 1 : eventDate.getDay() + 1; //add one because css-grid isn't zero-index'd + +export const getGridEndIndex = (eventEndDate: Date, endOfWeek: Date) => { + if (eventEndDate > endOfWeek) return 8; + const end = eventEndDate.getDay() + 2; // add two because css-grid isn't zero index'd, and day of week is zero-index'd + return end; +}; + /** * This component controls the layout of an individual events within a week * @returns EventLayout Component */ export const EventWeekLayout = ({ events, daysInweek }: EventLayoutProps) => { - const { - dateAdapter: { getGridStartIndex, getGridEndIndex }, - } = useCalendar(); const { eventComponent: EventComponent } = useComponents(); const { setHighlight, clearHighlight, isHighlighted } = useEventHighlight(); const { onEventClick } = useActions(); diff --git a/packages/Schedulely/src/layouts/headerLayout/HeaderLayout.tsx b/packages/Schedulely/src/layouts/headerLayout/HeaderLayout.tsx index 3da949d4..55006f4d 100644 --- a/packages/Schedulely/src/layouts/headerLayout/HeaderLayout.tsx +++ b/packages/Schedulely/src/layouts/headerLayout/HeaderLayout.tsx @@ -1,53 +1,28 @@ -import { - useActions, - useBreakpoint, - useCalendar, - useComponents, - useKeyboardControls, -} from '@/hooks'; -import { useEffect } from 'react'; +import { useBreakpoint, useCalendar, useComponents } from '@/hooks'; /** - * This component controls the layout of the header that display the controls and the current month/year description + * This component controls the layout of the header that display the controls and the current month/year description. + * This is just a wrapper so we can access context and pass it into user supplied components * @returns HeaderLayout component */ export const HeaderLayout = () => { const { - currentMonth, onNextMonth, onNextYear, onPrevYear, onPrevMonth, - dateAdapter: { getMonthName, getYear, isCurrentMonth }, + currentMonth, + currentYear, + isCurrentMonth, } = useCalendar(); - const { onMonthChangeClick } = useActions(); const { headerComponent: Header } = useComponents(); const { breakpoint } = useBreakpoint(); - useEffect(() => { - const firstOfMonth = new Date( - currentMonth.getFullYear(), - currentMonth.getMonth(), - 1 - ); - const lastOfMonth = new Date( - firstOfMonth.getFullYear(), - firstOfMonth.getMonth() + 1, - 1, - 0, - 0, - -1 - ); - onMonthChangeClick(firstOfMonth, lastOfMonth); - }, [currentMonth, onMonthChangeClick]); - - useKeyboardControls(); - return (
{ const { calendarWithEvents } = useCalendar(); + useKeyboardControls(); return (
{calendarWithEvents.map((week, idx) => (
@@ -24,10 +25,7 @@ export const MonthLayout = () => { events={week.events} daysInweek={week.daysInWeek} /> - +
))} diff --git a/packages/Schedulely/src/layouts/weekLayout/WeekLayout.tsx b/packages/Schedulely/src/layouts/weekLayout/WeekLayout.tsx index 4ba77a5e..17309880 100644 --- a/packages/Schedulely/src/layouts/weekLayout/WeekLayout.tsx +++ b/packages/Schedulely/src/layouts/weekLayout/WeekLayout.tsx @@ -1,4 +1,3 @@ -import { InternalCalendarEvent } from '@/types/InternalCalendarEvent'; import { useActions, useCalendar, @@ -6,41 +5,43 @@ import { useEventIntersection, } from '@/hooks'; -interface WeekLayoutProps { +export interface WeekLayoutProps { dates: Date[]; - eventsOnDays: { date: Date; events: InternalCalendarEvent[] }[]; } /** * This component controls the layout of an individual weeks worth of days * @returns WeekLayout Component */ -export const WeekLayout = ({ dates, eventsOnDays }: WeekLayoutProps) => { - const { dateAdapter, currentMonth } = useCalendar(); +export const WeekLayout = ({ dates }: WeekLayoutProps) => { + const { + dateAdapter: { isDateToday, isSameMonth, getDayNumber }, + currentDate, + } = useCalendar(); const { dayComponent: DayComponent } = useComponents(); const { onMoreEventClick } = useActions(); const { getEventsOnDate } = useEventIntersection(); return (
- {dates.map((day) => ( -
- !x.visible).length > 0 - } - events={getEventsOnDate(day)} - onClick={onMoreEventClick} - /> -
- ))} + {dates.map((day) => { + const isToday = isDateToday(day); + const events = getEventsOnDate(day); + const date = day.getDate(); + + return ( +
+ !x.visible).length > 0} + events={events} + onClick={onMoreEventClick} + /> +
+ ); + })}
); }; diff --git a/packages/Schedulely/src/providers/ActionProvider.tsx b/packages/Schedulely/src/providers/ActionProvider.tsx index 6de2f839..3f41b41a 100644 --- a/packages/Schedulely/src/providers/ActionProvider.tsx +++ b/packages/Schedulely/src/providers/ActionProvider.tsx @@ -1,11 +1,11 @@ -import { ActionState, InternalCalendarEvent } from '@/types'; +import { ActionContextState } from '@/types'; import { PropsWithChildren, createContext, useCallback } from 'react'; -export const ActionContext = createContext(null); +export const ActionContext = createContext(null); ActionContext.displayName = 'ActionContext'; interface ActionProviderProps { - actions?: Partial; + actions?: Partial; } /** @@ -19,33 +19,24 @@ export const ActionProvider = ({ children, actions, }: PropsWithChildren) => { - const onEventClick = actions?.onEventClick - ? actions?.onEventClick - : (event: InternalCalendarEvent) => console.log(event); - - const onMoreEventClick = actions?.onMoreEventClick - ? actions?.onMoreEventClick - : (events: InternalCalendarEvent[]) => console.log(events); + const onEventClick = useCallback(actions?.onEventClick || (() => null), [ + actions?.onEventClick, + ]); - const onMonthChangeClick = actions?.onMonthChangeClick - ? actions?.onMonthChangeClick - : (firstOfMonth: Date, lastOfMonth: Date) => - console.log({ firstOfMonth, lastOfMonth }); + const onMoreEventClick = useCallback( + actions?.onMoreEventClick || (() => null), + [actions?.onMoreEventClick] + ); - const memoizedOnEventClick = useCallback(onEventClick, [onEventClick]); + const onMonthChangeClick = useCallback( + actions?.onMonthChangeClick || (() => null), + [actions?.onMonthChangeClick] + ); - const memoizedOnMoreEventClick = useCallback(onMoreEventClick, [ + const context: ActionContextState = { + onEventClick, onMoreEventClick, - ]); - - const memoizedOnMonthChangeClick = useCallback(onMonthChangeClick, [ onMonthChangeClick, - ]); - - const context: ActionState = { - onEventClick: memoizedOnEventClick, - onMoreEventClick: memoizedOnMoreEventClick, - onMonthChangeClick: memoizedOnMonthChangeClick, }; return ( diff --git a/packages/Schedulely/src/providers/CalendarProvider.tsx b/packages/Schedulely/src/providers/CalendarProvider.tsx index e0bc17ba..0da5fab9 100644 --- a/packages/Schedulely/src/providers/CalendarProvider.tsx +++ b/packages/Schedulely/src/providers/CalendarProvider.tsx @@ -1,7 +1,6 @@ import { + CalendarContextState, CalendarEvent, - CalendarState, - ComponentSize, DateTimeAdapter, InternalCalendarEvent, InternalEventWeek, @@ -10,11 +9,14 @@ import { PropsWithChildren, createContext, useCallback, + useEffect, useMemo, useState, } from 'react'; +import { useActions } from '@/hooks/useActions'; +import { useBreakpoint } from '@/hooks/useBreakpoint'; -export const CalendarContext = createContext(null); +export const CalendarContext = createContext(null); CalendarContext.displayName = 'CalendarContext'; interface CalendarProviderProps { @@ -34,18 +36,36 @@ export const CalendarProvider = ({ calendarEvents, children, }: PropsWithChildren) => { - const [currentMonth, setCurrentMonth] = useState( + const { onMonthChangeClick } = useActions(); + const { breakpoint } = useBreakpoint(); + + const [currentDate, setCurrentDate] = useState( dateAdapter.convertIsoToDate(initialDate) ); - const getDaysOfWeek = useCallback( - (componentSize: ComponentSize) => dateAdapter.getDaysOfWeek(componentSize), - [dateAdapter] + const currentMonth = useMemo( + () => dateAdapter.getMonthName(currentDate), + [currentDate] + ); + + const currentYear = useMemo( + () => dateAdapter.getYear(currentDate), + [currentDate] + ); + + const isCurrentMonth = useMemo( + () => dateAdapter.isCurrentMonth(currentDate), + [currentDate] + ); + + const daysOfWeek = useMemo( + () => dateAdapter.getDaysOfWeek(breakpoint), + [breakpoint] ); const calendarView = useMemo( - () => dateAdapter.getCalendarView(currentMonth), - [currentMonth, dateAdapter] + () => dateAdapter.getCalendarView(currentDate), + [currentDate, dateAdapter] ); const events = useMemo( @@ -64,17 +84,24 @@ export const CalendarProvider = ({ }) .filter( (event) => - dateAdapter.isSameMonth(event.start, currentMonth) || - dateAdapter.isSameMonth(event.end, currentMonth) + dateAdapter.isSameMonth(event.start, currentDate) || + dateAdapter.isSameMonth(event.end, currentDate) ), - [currentMonth, calendarEvents, dateAdapter] + [currentDate, calendarEvents, dateAdapter] ); + useEffect(() => { + const firstDateInView = calendarView[0][0]; + const lastDateInView = + calendarView[calendarView.length - 1][ + calendarView[calendarView.length - 1].length - 1 + ]; + onMonthChangeClick(firstDateInView, lastDateInView); + }, [onMonthChangeClick, calendarView]); + const calendarWithEvents = useMemo( () => calendarView.map((week) => ({ - weekStart: week[0], - weekEnd: week[6], daysInWeek: week, events: events.filter((event) => dateAdapter.isEventInWeek(event.start, event.end, week) @@ -90,34 +117,37 @@ export const CalendarProvider = ({ ); const onNextMonth = useCallback( - () => setCurrentMonth((month) => dateAdapter.addMonthsToDate(month, 1)), + () => setCurrentDate((month) => dateAdapter.addMonthsToDate(month, 1)), [dateAdapter] ); const onNextYear = useCallback( - () => setCurrentMonth((month) => dateAdapter.addMonthsToDate(month, 12)), + () => setCurrentDate((month) => dateAdapter.addMonthsToDate(month, 12)), [dateAdapter] ); const onPrevMonth = useCallback( - () => setCurrentMonth((month) => dateAdapter.addMonthsToDate(month, -1)), + () => setCurrentDate((month) => dateAdapter.addMonthsToDate(month, -1)), [dateAdapter] ); const onPrevYear = useCallback( - () => setCurrentMonth((month) => dateAdapter.addMonthsToDate(month, -12)), + () => setCurrentDate((month) => dateAdapter.addMonthsToDate(month, -12)), [dateAdapter] ); - const contextValue: CalendarState = { + const contextValue: CalendarContextState = { + currentDate, currentMonth, + currentYear, dateAdapter, - getDaysOfWeek, + isCurrentMonth, + daysOfWeek, + calendarWithEvents, onNextMonth, onNextYear, onPrevMonth, onPrevYear, - calendarWithEvents, }; return ( diff --git a/packages/Schedulely/src/providers/HightlightProvider.tsx b/packages/Schedulely/src/providers/HightlightProvider.tsx index 05816e78..84f20038 100644 --- a/packages/Schedulely/src/providers/HightlightProvider.tsx +++ b/packages/Schedulely/src/providers/HightlightProvider.tsx @@ -1,7 +1,8 @@ -import { HighlightEventState } from '@/types'; +import { HighlightEventContextState } from '@/types'; import { ReactNode, createContext, useCallback, useState } from 'react'; -export const HighlightContext = createContext(null); +export const HighlightContext = + createContext(null); HighlightContext.displayName = 'HighlightContext'; /** @@ -19,7 +20,7 @@ export const HighlightProvider = ({ children }: { children: ReactNode }) => { [highlightedEvent] ); - const context: HighlightEventState = { + const context: HighlightEventContextState = { setHighlight: (eventId: string) => setHighlightedEvent(eventId), clearHighlight: () => setHighlightedEvent(undefined), isHighlighted, diff --git a/packages/Schedulely/src/types/ComponentSize.ts b/packages/Schedulely/src/types/ComponentSize.ts index a68c5131..5ec902a7 100644 --- a/packages/Schedulely/src/types/ComponentSize.ts +++ b/packages/Schedulely/src/types/ComponentSize.ts @@ -1 +1,2 @@ +/** The current size of the root schedulely container, based on width */ export type ComponentSize = 'small' | 'medium' | 'large'; diff --git a/packages/Schedulely/src/types/DateAdapter.ts b/packages/Schedulely/src/types/DateAdapter.ts index a974d5e7..9f69f4f2 100644 --- a/packages/Schedulely/src/types/DateAdapter.ts +++ b/packages/Schedulely/src/types/DateAdapter.ts @@ -16,12 +16,6 @@ export interface DateTimeAdapter { /** Get full names of all days of the week */ getDaysOfWeek: (componentSize: ComponentSize) => string[]; - /** Get the day of week grid index for the end of the event. Used for positioning within the Week css-grid. */ - getGridEndIndex: (eventEndDate: Date, endOfWeek: Date) => number; - - /** Get the day of week index for the start of the event. Used for positioning within the Week css-grid */ - getGridStartIndex: (eventDate: Date, startOfWeek: Date) => number; - /** Get the full name of the month for a given date */ getMonthName: (date: Date) => string; diff --git a/packages/Schedulely/src/types/InternalEventWeek.ts b/packages/Schedulely/src/types/InternalEventWeek.ts index 941ab4f8..115cea96 100644 --- a/packages/Schedulely/src/types/InternalEventWeek.ts +++ b/packages/Schedulely/src/types/InternalEventWeek.ts @@ -1,9 +1,13 @@ import { InternalCalendarEvent } from '@/types'; +/** This represents a single calendar week, including both days, events, and their relationships */ export interface InternalEventWeek { - weekStart: Date; - weekEnd: Date; + /** The calendar days in the week */ daysInWeek: Date[]; + + /** The events that occur within the week */ events: InternalCalendarEvent[]; + + /** The days of the week, with the events that occur on the given date */ eventsOnDays: { date: Date; events: InternalCalendarEvent[] }[]; } diff --git a/packages/Schedulely/src/types/SchedulelyProps.ts b/packages/Schedulely/src/types/SchedulelyProps.ts index f35415bd..58c13e9a 100644 --- a/packages/Schedulely/src/types/SchedulelyProps.ts +++ b/packages/Schedulely/src/types/SchedulelyProps.ts @@ -1,5 +1,5 @@ import { - ActionState, + ActionContextState, CalendarEvent, DateTimeAdapter, SchedulelyComponents, @@ -26,7 +26,7 @@ export interface SchedulelyProps { dark?: boolean; /** Schedulely actions */ - actions?: Partial; + actions?: Partial; /** Initial Date that Schedulely should be opened to */ initialDate?: string; diff --git a/packages/Schedulely/src/types/state/ActionState.ts b/packages/Schedulely/src/types/state/ActionContextState.ts similarity index 79% rename from packages/Schedulely/src/types/state/ActionState.ts rename to packages/Schedulely/src/types/state/ActionContextState.ts index d6d65d8a..ecf1b239 100644 --- a/packages/Schedulely/src/types/state/ActionState.ts +++ b/packages/Schedulely/src/types/state/ActionContextState.ts @@ -1,12 +1,13 @@ import { InternalCalendarEvent } from '../InternalCalendarEvent'; /** Represents the state of the ActionProvider */ -export interface ActionState { +export interface ActionContextState { /** function that will run when an event is clicked on */ onEventClick: (event: InternalCalendarEvent) => void; /** function that will run when the 'more events' indicator is clicked on */ onMoreEventClick: (event: InternalCalendarEvent[]) => void; + /** function that will run whenever the month of the calendar is changed */ onMonthChangeClick: (firstOfMonth: Date, lastOfMonth: Date) => void; } diff --git a/packages/Schedulely/src/types/state/BreakpointContextState.ts b/packages/Schedulely/src/types/state/BreakpointContextState.ts index 463d949d..14c7ec74 100644 --- a/packages/Schedulely/src/types/state/BreakpointContextState.ts +++ b/packages/Schedulely/src/types/state/BreakpointContextState.ts @@ -1,5 +1,6 @@ import { ComponentSize } from '../ComponentSize'; export type BreakpointContextState = { + /** The current size of the root container */ breakpoint: ComponentSize; }; diff --git a/packages/Schedulely/src/types/state/CalendarState.ts b/packages/Schedulely/src/types/state/CalendarContextState.ts similarity index 57% rename from packages/Schedulely/src/types/state/CalendarState.ts rename to packages/Schedulely/src/types/state/CalendarContextState.ts index ce4b4431..a96a3e0b 100644 --- a/packages/Schedulely/src/types/state/CalendarState.ts +++ b/packages/Schedulely/src/types/state/CalendarContextState.ts @@ -1,16 +1,24 @@ -import { ComponentSize } from '../ComponentSize'; import { DateTimeAdapter } from '..'; import { InternalEventWeek } from '../InternalEventWeek'; -export type CalendarState = { - /** The current visible month */ - currentMonth: Date; +export type CalendarContextState = { + /** The current active date (this controls calendar position) */ + currentDate: Date; + + /** The current active month */ + currentMonth: string; + + /** The current active year */ + currentYear: number; /** The DateTimeAdapter that is being utilized */ dateAdapter: DateTimeAdapter; - /** Array with localized names of the days of the week */ - getDaysOfWeek: (componentSize: ComponentSize) => string[]; + /** Indicates if the active month is the current month */ + isCurrentMonth: boolean; + + /** The days of the week, for use in the calendar header */ + daysOfWeek: string[]; /** Advance the calendar one month */ onNextMonth: () => void; diff --git a/packages/Schedulely/src/types/state/EventIntersectionContextState.ts b/packages/Schedulely/src/types/state/EventIntersectionContextState.ts index cbd0faf3..f536b32c 100644 --- a/packages/Schedulely/src/types/state/EventIntersectionContextState.ts +++ b/packages/Schedulely/src/types/state/EventIntersectionContextState.ts @@ -8,6 +8,6 @@ export type EventIntersectionState = { setRefFromKey: (key: string) => (element: HTMLElement | null) => void; /** Lookup an event and retrieve its visibility */ isEventVisible: (key: string) => boolean; - /** Gets an array of hidden overflow events for the supplied date. An empty array is returned if no days are hidden. */ + /** Gets an array of events that occur on or span the supplied date. */ getEventsOnDate: (date: Date) => InternalCalendarEvent[]; }; diff --git a/packages/Schedulely/src/types/state/HighlightEventState.ts b/packages/Schedulely/src/types/state/HighlightEventContextState.ts similarity index 86% rename from packages/Schedulely/src/types/state/HighlightEventState.ts rename to packages/Schedulely/src/types/state/HighlightEventContextState.ts index 4f075fe6..55c0802d 100644 --- a/packages/Schedulely/src/types/state/HighlightEventState.ts +++ b/packages/Schedulely/src/types/state/HighlightEventContextState.ts @@ -1,4 +1,4 @@ -export interface HighlightEventState { +export interface HighlightEventContextState { /** Set the ID of the currently highlighted event */ setHighlight: (eventId: string) => void; diff --git a/packages/Schedulely/src/types/state/index.ts b/packages/Schedulely/src/types/state/index.ts index 0de9ed78..3ef4d0fe 100644 --- a/packages/Schedulely/src/types/state/index.ts +++ b/packages/Schedulely/src/types/state/index.ts @@ -2,8 +2,8 @@ * @file Automatically generated by barrelsby. */ -export * from './ActionState'; +export * from './ActionContextState'; export * from './BreakpointContextState'; -export * from './CalendarState'; +export * from './CalendarContextState'; export * from './EventIntersectionContextState'; -export * from './HighlightEventState'; +export * from './HighlightEventContextState'; diff --git a/turbo.json b/turbo.json index ea98d154..37d9a360 100644 --- a/turbo.json +++ b/turbo.json @@ -13,6 +13,7 @@ }, "test": { "dependsOn": [ + "lint", "barrels" ], "outputs": [], @@ -53,7 +54,13 @@ ] }, "lint": { - "outputs": [] + "outputs": [], + "inputs": [ + "./src/**/*.tsx", + "./src/**/*.ts", + "./__tests__/**/*.tsx", + "./__tests__/**/*.ts" + ] }, "dev": { "cache": false