uikit-react-public 0.11.16 → 0.11.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/Calendar/Calendar.d.ts +3 -0
- package/dist/components/Calendar/Calendar.stories.d.ts +42 -0
- package/dist/components/Calendar/Calendar.types.d.ts +18 -0
- package/dist/components/Calendar/index.d.ts +2 -0
- package/dist/components/Calendar/subcomponents/AcademicWeek.d.ts +5 -0
- package/dist/components/Calendar/subcomponents/AcademicWeeks.d.ts +7 -0
- package/dist/components/Calendar/subcomponents/ColumnHeading.d.ts +7 -0
- package/dist/components/Calendar/subcomponents/Controls.d.ts +6 -0
- package/dist/components/Calendar/subcomponents/Day.d.ts +12 -0
- package/dist/components/{Datepicker/subcomponents/Day → Calendar/subcomponents}/Day.stories.d.ts +9 -1
- package/dist/components/Calendar/subcomponents/EventDot.d.ts +6 -0
- package/dist/components/Calendar/subcomponents/Grid.d.ts +11 -0
- package/dist/components/Calendar/subcomponents/index.d.ts +7 -0
- package/dist/components/Calendar/utils/getAcademicWeekNumbers/getAcademicWeekNumbers.d.ts +24 -0
- package/dist/components/Calendar/utils/index.d.ts +4 -0
- package/dist/components/Calendar/utils/normaliseMonth/normaliseMonth.d.ts +9 -0
- package/dist/components/Calendar/utils/normaliseMonth/normaliseMonth.test.d.ts +1 -0
- package/dist/components/Calendar/utils/parseDateFromString/parseDateFromString.d.ts +9 -0
- package/dist/components/Calendar/utils/parseDateFromString/parseDateFromString.test.d.ts +1 -0
- package/dist/components/Datepicker/Datepicker.d.ts +3 -12
- package/dist/components/Datepicker/Datepicker.stories.d.ts +16 -3
- package/dist/components/Datepicker/Datepicker.types.d.ts +23 -0
- package/dist/components/Datepicker/index.d.ts +1 -1
- package/dist/components/Datepicker/subcomponents/CustomDatepicker.d.ts +17 -0
- package/dist/components/Datepicker/subcomponents/DatepickerInput.d.ts +10 -0
- package/dist/components/Datepicker/subcomponents/NativeDatepicker.d.ts +6 -0
- package/dist/components/Datepicker/subcomponents/Panel.d.ts +6 -0
- package/dist/components/Datepicker/subcomponents/VisibleField.d.ts +12 -0
- package/dist/components/Datepicker/subcomponents/index.d.ts +5 -7
- package/dist/components/Datepicker/utils/dateToLocaleISOString/dateToLocaleISOString.d.ts +17 -0
- package/dist/components/Datepicker/utils/dateToLocaleISOString/dateToLocaleISOString.test.d.ts +1 -0
- package/dist/components/Datepicker/utils/index.d.ts +2 -2
- package/dist/components/Datepicker/utils/parseInputValue/parseInputValue.d.ts +11 -0
- package/dist/components/Datepicker/utils/parseInputValue/parseInputValue.test.d.ts +1 -0
- package/dist/components/Footer/Footer.d.ts +1 -1
- package/dist/components/Header/Header.d.ts +5 -4
- package/dist/components/Header/index.d.ts +1 -1
- package/dist/components/Menu/Menu.d.ts +2 -1
- package/dist/components/Menu/MenuContent.d.ts +1 -0
- package/dist/components/Select/Select.stories.d.ts +1 -1
- package/dist/components/Select/Select.types.d.ts +10 -50
- package/dist/components/Select/index.d.ts +1 -1
- package/dist/components/Select/subcomponents/CustomSelect.d.ts +2 -1
- package/dist/components/index.d.ts +2 -0
- package/dist/index.js +3332 -3063
- package/lib/components/Calendar/Calendar.stories.tsx +209 -0
- package/lib/components/Calendar/Calendar.tsx +121 -0
- package/lib/components/Calendar/Calendar.types.ts +21 -0
- package/lib/components/Calendar/__tests__/Calendar.test.tsx +71 -0
- package/lib/components/Calendar/__tests__/__snapshots__/Calendar.test.tsx.snap +1218 -0
- package/lib/components/Calendar/index.ts +6 -0
- package/lib/components/Calendar/subcomponents/AcademicWeek.tsx +36 -0
- package/lib/components/Calendar/subcomponents/AcademicWeeks.tsx +46 -0
- package/lib/components/Calendar/subcomponents/ColumnHeading.tsx +40 -0
- package/lib/components/{Datepicker/subcomponents/MonthSelector/MonthSelector.tsx → Calendar/subcomponents/Controls.tsx} +17 -17
- package/lib/components/{Datepicker/subcomponents/Day → Calendar/subcomponents}/Day.stories.tsx +30 -7
- package/lib/components/Calendar/subcomponents/Day.tsx +130 -0
- package/lib/components/Calendar/subcomponents/EventDot.tsx +40 -0
- package/lib/components/Calendar/subcomponents/Grid.tsx +117 -0
- package/lib/components/Calendar/subcomponents/index.ts +7 -0
- package/lib/components/Calendar/utils/getAcademicWeekNumbers/getAcademicWeekNumbers.test.ts +104 -0
- package/lib/components/Calendar/utils/getAcademicWeekNumbers/getAcademicWeekNumbers.ts +85 -0
- package/lib/components/{Datepicker → Calendar}/utils/getDatesForCalendarGrid/getDatesForCalendarGrid.test.ts +29 -65
- package/lib/components/{Datepicker → Calendar}/utils/getDatesForCalendarGrid/getDatesForCalendarGrid.ts +11 -43
- package/lib/components/Calendar/utils/index.ts +4 -0
- package/lib/components/Calendar/utils/normaliseMonth/normaliseMonth.test.ts +40 -0
- package/lib/components/Calendar/utils/normaliseMonth/normaliseMonth.ts +16 -0
- package/lib/components/Calendar/utils/parseDateFromString/parseDateFromString.test.ts +15 -0
- package/lib/components/Calendar/utils/parseDateFromString/parseDateFromString.ts +19 -0
- package/lib/components/Datepicker/Datepicker.stories.tsx +220 -23
- package/lib/components/Datepicker/Datepicker.tsx +34 -137
- package/lib/components/Datepicker/Datepicker.types.ts +38 -0
- package/lib/components/Datepicker/__tests__/Datepicker.test.tsx +53 -112
- package/lib/components/Datepicker/__tests__/__snapshots__/Datepicker.test.tsx.snap +92 -638
- package/lib/components/Datepicker/index.ts +1 -1
- package/lib/components/Datepicker/subcomponents/CustomDatepicker.tsx +209 -0
- package/lib/components/Datepicker/subcomponents/DatepickerInput.tsx +74 -0
- package/lib/components/Datepicker/subcomponents/NativeDatepicker.tsx +70 -0
- package/lib/components/Datepicker/subcomponents/Panel.tsx +32 -0
- package/lib/components/Datepicker/subcomponents/VisibleField.tsx +104 -0
- package/lib/components/Datepicker/subcomponents/index.ts +5 -7
- package/lib/components/Datepicker/utils/dateToLocaleISOString/dateToLocaleISOString.test.ts +32 -0
- package/lib/components/Datepicker/utils/dateToLocaleISOString/dateToLocaleISOString.ts +23 -0
- package/lib/components/Datepicker/utils/index.ts +2 -2
- package/lib/components/Datepicker/utils/parseInputValue/parseInputValue.test.ts +110 -0
- package/lib/components/Datepicker/utils/parseInputValue/parseInputValue.ts +57 -0
- package/lib/components/Footer/Footer.tsx +3 -3
- package/lib/components/Footer/__tests__/__snapshots__/Footer.test.tsx.snap +6 -6
- package/lib/components/Header/Header.tsx +32 -33
- package/lib/components/Header/HeaderMenu.tsx +9 -2
- package/lib/components/Header/__tests__/__snapshots__/Header.test.tsx.snap +40 -48
- package/lib/components/Header/index.ts +5 -1
- package/lib/components/Menu/Menu.tsx +3 -0
- package/lib/components/Menu/MenuContent.tsx +4 -1
- package/lib/components/Select/Select.stories.tsx +38 -39
- package/lib/components/Select/Select.tsx +4 -18
- package/lib/components/Select/Select.types.ts +30 -69
- package/lib/components/Select/__tests__/Select.test.tsx +6 -6
- package/lib/components/Select/__tests__/__snapshots__/Select.test.tsx.snap +1 -1
- package/lib/components/Select/index.ts +1 -1
- package/lib/components/Select/subcomponents/CustomSelect.tsx +22 -12
- package/lib/components/Select/subcomponents/NativeSelect.tsx +7 -3
- package/lib/components/Select/subcomponents/Panel.tsx +4 -4
- package/lib/components/Select/subcomponents/VisibleField.tsx +1 -1
- package/lib/components/index.ts +3 -0
- package/package.json +4 -4
- package/LICENSE +0 -9
- package/dist/components/Datepicker/subcomponents/CalendarGrid/CalendarGrid.d.ts +0 -6
- package/dist/components/Datepicker/subcomponents/CalendarGrid/index.d.ts +0 -1
- package/dist/components/Datepicker/subcomponents/CalendarMenu/CalendarMenu.d.ts +0 -8
- package/dist/components/Datepicker/subcomponents/CalendarMenu/index.d.ts +0 -1
- package/dist/components/Datepicker/subcomponents/ColumnHeadings/ColumnHeadings.d.ts +0 -2
- package/dist/components/Datepicker/subcomponents/ColumnHeadings/index.d.ts +0 -1
- package/dist/components/Datepicker/subcomponents/DateField/DateField.d.ts +0 -7
- package/dist/components/Datepicker/subcomponents/DateField/index.d.ts +0 -1
- package/dist/components/Datepicker/subcomponents/Day/Day.d.ts +0 -10
- package/dist/components/Datepicker/subcomponents/Day/index.d.ts +0 -1
- package/dist/components/Datepicker/subcomponents/MonthSelector/MonthSelector.d.ts +0 -6
- package/dist/components/Datepicker/subcomponents/MonthSelector/index.d.ts +0 -1
- package/dist/components/Datepicker/subcomponents/Native/Native.d.ts +0 -9
- package/dist/components/Datepicker/subcomponents/Native/index.d.ts +0 -1
- package/dist/components/Datepicker/utils/parseDateForDateField/parseDateForDateField.d.ts +0 -20
- package/lib/components/Datepicker/subcomponents/CalendarGrid/CalendarGrid.tsx +0 -59
- package/lib/components/Datepicker/subcomponents/CalendarGrid/index.ts +0 -1
- package/lib/components/Datepicker/subcomponents/CalendarMenu/CalendarMenu.tsx +0 -64
- package/lib/components/Datepicker/subcomponents/CalendarMenu/index.ts +0 -1
- package/lib/components/Datepicker/subcomponents/ColumnHeadings/ColumnHeadings.tsx +0 -35
- package/lib/components/Datepicker/subcomponents/ColumnHeadings/index.ts +0 -1
- package/lib/components/Datepicker/subcomponents/DateField/DateField.tsx +0 -155
- package/lib/components/Datepicker/subcomponents/DateField/__tests__/DateField.test.tsx +0 -191
- package/lib/components/Datepicker/subcomponents/DateField/index.ts +0 -1
- package/lib/components/Datepicker/subcomponents/Day/Day.tsx +0 -94
- package/lib/components/Datepicker/subcomponents/Day/index.ts +0 -1
- package/lib/components/Datepicker/subcomponents/MonthSelector/index.ts +0 -1
- package/lib/components/Datepicker/subcomponents/Native/Native.tsx +0 -59
- package/lib/components/Datepicker/subcomponents/Native/index.ts +0 -1
- package/lib/components/Datepicker/utils/parseDateForDateField/parseDateForDateField.test.ts +0 -41
- package/lib/components/Datepicker/utils/parseDateForDateField/parseDateForDateField.ts +0 -48
- /package/dist/components/{Datepicker/subcomponents/DateField/__tests__/DateField.test.d.ts → Calendar/__tests__/Calendar.test.d.ts} +0 -0
- /package/dist/components/{Datepicker/utils/getDatesForCalendarGrid/getDatesForCalendarGrid.test.d.ts → Calendar/utils/getAcademicWeekNumbers/getAcademicWeekNumbers.test.d.ts} +0 -0
- /package/dist/components/{Datepicker → Calendar}/utils/getDatesForCalendarGrid/getDatesForCalendarGrid.d.ts +0 -0
- /package/dist/components/{Datepicker/utils/parseDateForDateField/parseDateForDateField.test.d.ts → Calendar/utils/getDatesForCalendarGrid/getDatesForCalendarGrid.test.d.ts} +0 -0
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import type { AcademicWeek } from '../../';
|
|
2
|
+
|
|
3
|
+
// The 'calendar period' is defined here as all the dates the calendar displays,
|
|
4
|
+
// which may include some dates in the previous month and some dates in the next month.
|
|
5
|
+
// This is because we display complete weeks in the calendar.
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Returns the Monday for the week of the given date.
|
|
9
|
+
* For example:
|
|
10
|
+
* - If the input date is a Wednesday, it returns the Monday of that week.
|
|
11
|
+
* - If the input date is already a Monday, it returns that date.
|
|
12
|
+
* @param {Date} d - The input date.
|
|
13
|
+
* @returns {Date} The Monday of the week of the input date.
|
|
14
|
+
*/
|
|
15
|
+
export const getMonday = (d: Date) => {
|
|
16
|
+
const date = new Date(d);
|
|
17
|
+
const day = date.getDay();
|
|
18
|
+
const delta = (day + 6) % 7;
|
|
19
|
+
date.setDate(date.getDate() - delta);
|
|
20
|
+
date.setHours(0, 0, 0, 0);
|
|
21
|
+
return date;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Calculates the academic week numbers for each week displayed in a calendar month.
|
|
26
|
+
* Used by `<AcademicWeeks />` component in the Datepicker.
|
|
27
|
+
* It determines the Mondays of each week shown in the calendar for the `targetDate`'s month.
|
|
28
|
+
* Then, it maps these Mondays to the corresponding academic week number from the `academicWeeks` array.
|
|
29
|
+
* If an academic week is not found for a given Monday, `undefined` is used, in place in the returned array.
|
|
30
|
+
*
|
|
31
|
+
* @param {AcademicWeek[]} academicWeeks - An array of academic week objects, each with a `start` date and `number`.
|
|
32
|
+
* @param {Date} targetDate - The date used to generate the calendar display.
|
|
33
|
+
* @returns {(number | undefined)[]} An array of academic week numbers or `undefined` for each week in the calendar display.
|
|
34
|
+
* Returns an empty array if `academicWeeks` is empty or `targetDate` is invalid, with console warnings, in the interest of safe usage.
|
|
35
|
+
*/
|
|
36
|
+
const getAcademicWeekNumbers = (
|
|
37
|
+
academicWeeks: AcademicWeek[],
|
|
38
|
+
targetDate: Date
|
|
39
|
+
) => {
|
|
40
|
+
if (!academicWeeks || academicWeeks.length === 0) {
|
|
41
|
+
console.warn('Datepicker: No academic weeks provided');
|
|
42
|
+
return [];
|
|
43
|
+
}
|
|
44
|
+
if (
|
|
45
|
+
!targetDate ||
|
|
46
|
+
!(targetDate instanceof Date) ||
|
|
47
|
+
Number.isNaN(targetDate.getTime())
|
|
48
|
+
) {
|
|
49
|
+
console.warn('Datepicker: Invalid target date provided');
|
|
50
|
+
return [];
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Normalise to the first Monday of calendar period
|
|
54
|
+
const targetYear = targetDate.getFullYear();
|
|
55
|
+
const targetMonth = targetDate.getMonth();
|
|
56
|
+
const firstOfMonth = new Date(targetYear, targetMonth, 1);
|
|
57
|
+
const firstMonday = getMonday(firstOfMonth);
|
|
58
|
+
|
|
59
|
+
const calendarWeeks: Date[] = [];
|
|
60
|
+
calendarWeeks.push(firstMonday);
|
|
61
|
+
|
|
62
|
+
// Increment to the next Monday of the calendar period
|
|
63
|
+
const nextMonday = new Date(firstMonday);
|
|
64
|
+
nextMonday.setDate(firstMonday.getDate() + 7);
|
|
65
|
+
|
|
66
|
+
// Then keep incrementing until the end of the calendar period
|
|
67
|
+
while (nextMonday.getMonth() === targetMonth) {
|
|
68
|
+
calendarWeeks.push(new Date(nextMonday));
|
|
69
|
+
nextMonday.setDate(nextMonday.getDate() + 7);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// We need an array of actual week numbers to pass to the UI
|
|
73
|
+
const weekNumbers: (number | undefined)[] = calendarWeeks.map((date) => {
|
|
74
|
+
// `Array.find()` will return `undefined` if no match is found.
|
|
75
|
+
// `undefined` is used by `<AcademicWeek />` for row placement.
|
|
76
|
+
const matchedWeek = academicWeeks.find(
|
|
77
|
+
(week) => new Date(week.start).toDateString() === date.toDateString()
|
|
78
|
+
);
|
|
79
|
+
return matchedWeek?.number;
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
return weekNumbers;
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
export default getAcademicWeekNumbers;
|
|
@@ -1,78 +1,51 @@
|
|
|
1
1
|
import { describe, expect, test } from 'vitest';
|
|
2
2
|
import getDatesForCalendarGrid from './getDatesForCalendarGrid';
|
|
3
3
|
|
|
4
|
+
// Note by Alex HF (2025-07-04): Some of these tests seem a bit excessive.
|
|
5
|
+
// I pasted them in from my orginal <Datepicker> work.
|
|
6
|
+
|
|
4
7
|
describe('getDatesForCalendarGrid', () => {
|
|
5
8
|
test('Returns an array of Date objects', () => {
|
|
6
9
|
const result = getDatesForCalendarGrid(new Date());
|
|
7
|
-
expect(
|
|
8
|
-
result.every((date) => date instanceof Date)
|
|
9
|
-
).toBe(true);
|
|
10
|
+
expect(result.every((date) => date instanceof Date)).toBe(true);
|
|
10
11
|
});
|
|
11
12
|
|
|
12
13
|
test('Throws an error if no date is provided', () => {
|
|
13
14
|
// @ts-expect-error - Expected behaviour is an error to be thrown
|
|
14
|
-
expect(() => getDatesForCalendarGrid()).toThrowError(
|
|
15
|
+
expect(() => getDatesForCalendarGrid()).toThrowError('No date provided');
|
|
16
|
+
// @ts-expect-error - Expected behaviour is an error to be thrown
|
|
17
|
+
expect(() => getDatesForCalendarGrid(null)).toThrowError(
|
|
15
18
|
'No date provided'
|
|
16
19
|
);
|
|
17
|
-
expect(() =>
|
|
18
|
-
// @ts-expect-error - Expected behaviour is an error to be thrown
|
|
19
|
-
getDatesForCalendarGrid(null)
|
|
20
|
-
).toThrowError('No date provided');
|
|
21
|
-
expect(() =>
|
|
22
|
-
// @ts-expect-error - Expected behaviour is an error to be thrown
|
|
23
|
-
getDatesForCalendarGrid(undefined)
|
|
24
|
-
).toThrowError('No date provided');
|
|
25
20
|
// @ts-expect-error - Expected behaviour is an error to be thrown
|
|
26
|
-
expect(() => getDatesForCalendarGrid(
|
|
21
|
+
expect(() => getDatesForCalendarGrid(undefined)).toThrowError(
|
|
27
22
|
'No date provided'
|
|
28
23
|
);
|
|
29
24
|
// @ts-expect-error - Expected behaviour is an error to be thrown
|
|
30
|
-
expect(() => getDatesForCalendarGrid(
|
|
25
|
+
expect(() => getDatesForCalendarGrid('')).toThrowError('No date provided');
|
|
26
|
+
// @ts-expect-error - Expected behaviour is an error to be thrown
|
|
27
|
+
expect(() => getDatesForCalendarGrid(0)).toThrowError('No date provided');
|
|
28
|
+
// @ts-expect-error - Expected behaviour is an error to be thrown
|
|
29
|
+
expect(() => getDatesForCalendarGrid('1st of March')).toThrowError(
|
|
31
30
|
'No date provided'
|
|
32
31
|
);
|
|
33
|
-
expect(() =>
|
|
34
|
-
// @ts-expect-error - Expected behaviour is an error to be thrown
|
|
35
|
-
getDatesForCalendarGrid('1st of March')
|
|
36
|
-
).toThrowError('No date provided');
|
|
37
32
|
});
|
|
38
33
|
|
|
39
34
|
test('Total dates returned are always divisible by 7', () => {
|
|
40
|
-
expect(
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
).toBe(0);
|
|
44
|
-
expect(
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
).toBe(0);
|
|
48
|
-
expect(
|
|
49
|
-
getDatesForCalendarGrid(new Date(2025, 3, 15))
|
|
50
|
-
.length % 7
|
|
51
|
-
).toBe(0);
|
|
52
|
-
expect(
|
|
53
|
-
getDatesForCalendarGrid(new Date(2025, 4, 20))
|
|
54
|
-
.length % 7
|
|
55
|
-
).toBe(0);
|
|
56
|
-
expect(
|
|
57
|
-
getDatesForCalendarGrid(new Date(2025, 5, 25))
|
|
58
|
-
.length % 7
|
|
59
|
-
).toBe(0);
|
|
60
|
-
expect(
|
|
61
|
-
getDatesForCalendarGrid(new Date(2025, 6, 30))
|
|
62
|
-
.length % 7
|
|
63
|
-
).toBe(0);
|
|
64
|
-
expect(
|
|
65
|
-
getDatesForCalendarGrid(new Date(2025, 12, 0))
|
|
66
|
-
.length % 7
|
|
67
|
-
).toBe(0);
|
|
35
|
+
expect(getDatesForCalendarGrid(new Date(2025, 1, 1)).length % 7).toBe(0);
|
|
36
|
+
expect(getDatesForCalendarGrid(new Date(2025, 2, 5)).length % 7).toBe(0);
|
|
37
|
+
expect(getDatesForCalendarGrid(new Date(2025, 3, 15)).length % 7).toBe(0);
|
|
38
|
+
expect(getDatesForCalendarGrid(new Date(2025, 4, 20)).length % 7).toBe(0);
|
|
39
|
+
expect(getDatesForCalendarGrid(new Date(2025, 5, 25)).length % 7).toBe(0);
|
|
40
|
+
expect(getDatesForCalendarGrid(new Date(2025, 6, 30)).length % 7).toBe(0);
|
|
41
|
+
expect(getDatesForCalendarGrid(new Date(2025, 12, 0)).length % 7).toBe(0);
|
|
68
42
|
});
|
|
69
43
|
|
|
70
|
-
test('If date is in Jan 2025, returns all dates in January
|
|
44
|
+
test('If date is in Jan 2025, returns all dates in January 2025', () => {
|
|
71
45
|
const date = new Date(2025, 0, 1);
|
|
72
46
|
const result = getDatesForCalendarGrid(date);
|
|
73
47
|
const janDates = [];
|
|
74
|
-
for (let i = 1; i <= 31; i++)
|
|
75
|
-
janDates.push(new Date(2025, 0, i));
|
|
48
|
+
for (let i = 1; i <= 31; i++) janDates.push(new Date(2025, 0, i));
|
|
76
49
|
janDates.forEach((dateInJan) => {
|
|
77
50
|
expect(result).toContainEqual(dateInJan);
|
|
78
51
|
});
|
|
@@ -88,12 +61,8 @@ describe('getDatesForCalendarGrid', () => {
|
|
|
88
61
|
test('If date is in Jan 2025, returns 1st & 2nd Feb 2025 at the end', () => {
|
|
89
62
|
const date = new Date(2025, 0, 1);
|
|
90
63
|
const result = getDatesForCalendarGrid(date);
|
|
91
|
-
expect(result[result.length - 2]).toEqual(
|
|
92
|
-
|
|
93
|
-
);
|
|
94
|
-
expect(result[result.length - 1]).toEqual(
|
|
95
|
-
new Date(2025, 1, 2)
|
|
96
|
-
);
|
|
64
|
+
expect(result[result.length - 2]).toEqual(new Date(2025, 1, 1));
|
|
65
|
+
expect(result[result.length - 1]).toEqual(new Date(2025, 1, 2));
|
|
97
66
|
});
|
|
98
67
|
|
|
99
68
|
test('If month starts on a Sunday, the total dates returned are divisible by 7', () => {
|
|
@@ -101,12 +70,12 @@ describe('getDatesForCalendarGrid', () => {
|
|
|
101
70
|
const result = getDatesForCalendarGrid(date);
|
|
102
71
|
expect(result.length % 7).toBe(0);
|
|
103
72
|
});
|
|
73
|
+
|
|
104
74
|
test('handles February in leap year correctly', () => {
|
|
105
75
|
const leapYear = new Date(2024, 1, 1); // February 2024
|
|
106
76
|
const result = getDatesForCalendarGrid(leapYear);
|
|
107
77
|
const febDates = [];
|
|
108
|
-
for (let i = 1; i <= 29; i++)
|
|
109
|
-
febDates.push(new Date(2024, 1, i));
|
|
78
|
+
for (let i = 1; i <= 29; i++) febDates.push(new Date(2024, 1, i));
|
|
110
79
|
febDates.forEach((date) => {
|
|
111
80
|
expect(result).toContainEqual(date);
|
|
112
81
|
});
|
|
@@ -116,8 +85,7 @@ describe('getDatesForCalendarGrid', () => {
|
|
|
116
85
|
const nonLeapYear = new Date(2025, 1, 1); // February 2025
|
|
117
86
|
const result = getDatesForCalendarGrid(nonLeapYear);
|
|
118
87
|
const febDates = [];
|
|
119
|
-
for (let i = 1; i <= 28; i++)
|
|
120
|
-
febDates.push(new Date(2025, 1, i));
|
|
88
|
+
for (let i = 1; i <= 28; i++) febDates.push(new Date(2025, 1, i));
|
|
121
89
|
febDates.forEach((date) => {
|
|
122
90
|
expect(result).toContainEqual(date);
|
|
123
91
|
});
|
|
@@ -127,18 +95,14 @@ describe('getDatesForCalendarGrid', () => {
|
|
|
127
95
|
const date = new Date(2025, 3, 1); // April 2025
|
|
128
96
|
const result = getDatesForCalendarGrid(date);
|
|
129
97
|
for (let i = 1; i < result.length; i++) {
|
|
130
|
-
expect(result[i].getTime()).toBeGreaterThan(
|
|
131
|
-
result[i - 1].getTime()
|
|
132
|
-
);
|
|
98
|
+
expect(result[i].getTime()).toBeGreaterThan(result[i - 1].getTime());
|
|
133
99
|
}
|
|
134
100
|
});
|
|
135
101
|
|
|
136
102
|
test('correctly handles months with 31 days', () => {
|
|
137
103
|
const date = new Date(2025, 0, 1); // January 2025
|
|
138
104
|
const result = getDatesForCalendarGrid(date);
|
|
139
|
-
expect(
|
|
140
|
-
result.filter((d) => d.getMonth() === 0).length
|
|
141
|
-
).toBe(31);
|
|
105
|
+
expect(result.filter((d) => d.getMonth() === 0).length).toBe(31);
|
|
142
106
|
});
|
|
143
107
|
|
|
144
108
|
test('week always starts with Monday', () => {
|
|
@@ -17,24 +17,15 @@
|
|
|
17
17
|
* ```
|
|
18
18
|
*/
|
|
19
19
|
const getDatesForCalendarGrid = (date: Date): Date[] => {
|
|
20
|
-
if (!date || !(date instanceof Date))
|
|
21
|
-
throw new Error('No date provided');
|
|
20
|
+
if (!date || !(date instanceof Date)) throw new Error('No date provided');
|
|
22
21
|
|
|
23
22
|
const month = date.getMonth();
|
|
24
|
-
const daysInMonth = new Date(
|
|
25
|
-
date.getFullYear(),
|
|
26
|
-
month + 1,
|
|
27
|
-
0
|
|
28
|
-
).getDate();
|
|
23
|
+
const daysInMonth = new Date(date.getFullYear(), month + 1, 0).getDate();
|
|
29
24
|
|
|
30
25
|
// Get all days in current month
|
|
31
26
|
const dates: Date[] = [];
|
|
32
27
|
for (let day = 1; day <= daysInMonth; day++) {
|
|
33
|
-
const newDate = new Date(
|
|
34
|
-
date.getFullYear(),
|
|
35
|
-
month,
|
|
36
|
-
day
|
|
37
|
-
);
|
|
28
|
+
const newDate = new Date(date.getFullYear(), month, day);
|
|
38
29
|
if (newDate.getMonth() !== month) break;
|
|
39
30
|
dates.push(newDate);
|
|
40
31
|
}
|
|
@@ -43,28 +34,14 @@ const getDatesForCalendarGrid = (date: Date): Date[] => {
|
|
|
43
34
|
// Sunday (0) should become 6
|
|
44
35
|
// Monday (1) should become 0
|
|
45
36
|
// Tuesday (2) should become 1, etc.
|
|
46
|
-
const adjustDay = (day: number): number =>
|
|
47
|
-
day === 0 ? 6 : day - 1;
|
|
37
|
+
const adjustDay = (day: number): number => (day === 0 ? 6 : day - 1);
|
|
48
38
|
|
|
49
39
|
// Calculate previous month's "grey days"
|
|
50
40
|
const prevMonthGreyDays = [];
|
|
51
|
-
const prevMonth = new Date(
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
);
|
|
56
|
-
const firstDayOfMonth = new Date(
|
|
57
|
-
date.getFullYear(),
|
|
58
|
-
month,
|
|
59
|
-
1
|
|
60
|
-
).getDay();
|
|
61
|
-
const numberOfDaysFromPrevMonth =
|
|
62
|
-
adjustDay(firstDayOfMonth);
|
|
63
|
-
const totalDaysInPrevMonth = new Date(
|
|
64
|
-
date.getFullYear(),
|
|
65
|
-
month,
|
|
66
|
-
0
|
|
67
|
-
).getDate();
|
|
41
|
+
const prevMonth = new Date(date.getFullYear(), month - 1, 1);
|
|
42
|
+
const firstDayOfMonth = new Date(date.getFullYear(), month, 1).getDay();
|
|
43
|
+
const numberOfDaysFromPrevMonth = adjustDay(firstDayOfMonth);
|
|
44
|
+
const totalDaysInPrevMonth = new Date(date.getFullYear(), month, 0).getDate();
|
|
68
45
|
|
|
69
46
|
for (let i = numberOfDaysFromPrevMonth; i > 0; i--) {
|
|
70
47
|
prevMonthGreyDays.push(
|
|
@@ -78,26 +55,17 @@ const getDatesForCalendarGrid = (date: Date): Date[] => {
|
|
|
78
55
|
|
|
79
56
|
// Calculate next month's "grey days"
|
|
80
57
|
const nextMonthGreyDays = [];
|
|
81
|
-
const nextMonth = new Date(
|
|
82
|
-
date.getFullYear(),
|
|
83
|
-
month + 1,
|
|
84
|
-
1
|
|
85
|
-
);
|
|
58
|
+
const nextMonth = new Date(date.getFullYear(), month + 1, 1);
|
|
86
59
|
const lastDayOfMonth = new Date(
|
|
87
60
|
date.getFullYear(),
|
|
88
61
|
month,
|
|
89
62
|
daysInMonth
|
|
90
63
|
).getDay();
|
|
91
|
-
const numberOfDaysFromNextMonth =
|
|
92
|
-
6 - adjustDay(lastDayOfMonth);
|
|
64
|
+
const numberOfDaysFromNextMonth = 6 - adjustDay(lastDayOfMonth);
|
|
93
65
|
|
|
94
66
|
for (let i = 1; i <= numberOfDaysFromNextMonth; i++) {
|
|
95
67
|
nextMonthGreyDays.push(
|
|
96
|
-
new Date(
|
|
97
|
-
nextMonth.getFullYear(),
|
|
98
|
-
nextMonth.getMonth(),
|
|
99
|
-
i
|
|
100
|
-
)
|
|
68
|
+
new Date(nextMonth.getFullYear(), nextMonth.getMonth(), i)
|
|
101
69
|
);
|
|
102
70
|
}
|
|
103
71
|
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { default as normaliseMonth } from './normaliseMonth/normaliseMonth';
|
|
2
|
+
export { default as parseDateFromString } from './parseDateFromString/parseDateFromString';
|
|
3
|
+
export { default as getAcademicWeekNumbers } from './getAcademicWeekNumbers/getAcademicWeekNumbers';
|
|
4
|
+
export { default as getDatesForCalendarGrid } from './getDatesForCalendarGrid/getDatesForCalendarGrid';
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { describe, expect, test } from 'vitest';
|
|
2
|
+
import normaliseMonth from './normaliseMonth';
|
|
3
|
+
|
|
4
|
+
describe('Calendar: normaliseMonth', () => {
|
|
5
|
+
test('Should return null if given undefined', () => {
|
|
6
|
+
expect(normaliseMonth(undefined)).toBeNull();
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
test('Should return null if given null', () => {
|
|
10
|
+
expect(normaliseMonth(null)).toBeNull();
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
test('Returns a Date object set to the first of the month', () => {
|
|
14
|
+
const date = new Date('2025-03-10');
|
|
15
|
+
const normalisedDate = normaliseMonth(date);
|
|
16
|
+
expect(normalisedDate).toBeInstanceOf(Date);
|
|
17
|
+
expect(normalisedDate?.getFullYear()).toBe(2025);
|
|
18
|
+
expect(normalisedDate?.getMonth()).toBe(2); // March (0-indexed)
|
|
19
|
+
expect(normalisedDate?.getDate()).toBe(1);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
test('Works for a date within daylight saving time', () => {
|
|
23
|
+
const date = new Date('2025-06-15');
|
|
24
|
+
const normalisedDate = normaliseMonth(date);
|
|
25
|
+
expect(normalisedDate).toBeInstanceOf(Date);
|
|
26
|
+
expect(normalisedDate?.getFullYear()).toBe(2025);
|
|
27
|
+
expect(normalisedDate?.getMonth()).toBe(5); // June (0-indexed)
|
|
28
|
+
expect(normalisedDate?.getDate()).toBe(1);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
test('Returned date is normalised for midnight', () => {
|
|
32
|
+
const date = new Date('2025-01-01T12:34:56');
|
|
33
|
+
const normalisedDate = normaliseMonth(date);
|
|
34
|
+
expect(normalisedDate).toBeInstanceOf(Date);
|
|
35
|
+
expect(normalisedDate?.getFullYear()).toBe(2025);
|
|
36
|
+
expect(normalisedDate?.getMonth()).toBe(0); // January (0-indexed)
|
|
37
|
+
expect(normalisedDate?.getDate()).toBe(1);
|
|
38
|
+
expect(normalisedDate?.getHours()).toBe(0);
|
|
39
|
+
});
|
|
40
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Normalises a given date to the first day of its month (with the time set to midnight).
|
|
3
|
+
* If the input date is undefined, it returns null.
|
|
4
|
+
* Used in <Calendar> to track the displayed month.
|
|
5
|
+
* @param date The date to normalise.
|
|
6
|
+
* @returns A new Date object set to the first day of the month with the time at midnight, or null if the input was null or undefined.
|
|
7
|
+
*/
|
|
8
|
+
const normaliseMonth = (date: Date | null | undefined) => {
|
|
9
|
+
if (!date) return null;
|
|
10
|
+
const newDate = new Date(date);
|
|
11
|
+
newDate.setDate(1);
|
|
12
|
+
newDate.setHours(0, 0, 0, 0);
|
|
13
|
+
return newDate;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export default normaliseMonth;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { describe, expect, test } from 'vitest';
|
|
2
|
+
import parseDateFromString from './parseDateFromString';
|
|
3
|
+
|
|
4
|
+
describe('Calendar: parseDateFromString', () => {
|
|
5
|
+
test('Should return null for an invalid date string', () => {
|
|
6
|
+
expect(parseDateFromString('invalid-date')).toBeNull();
|
|
7
|
+
expect(parseDateFromString('')).toBeNull();
|
|
8
|
+
expect(parseDateFromString(' ')).toBeNull();
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
test('Should return a valid date for a valid date string', () => {
|
|
12
|
+
expect(parseDateFromString('2023-03-15')).toEqual(new Date('2023-03-15'));
|
|
13
|
+
expect(parseDateFromString('2025-06-30')).toEqual(new Date('2025-06-30'));
|
|
14
|
+
});
|
|
15
|
+
});
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parses a date string into a Date object.
|
|
3
|
+
* Returns null if the input is invalid.
|
|
4
|
+
*
|
|
5
|
+
* @param dateString - The date string to parse (can be in ISO format or UNIX timestamp).
|
|
6
|
+
* @returns A Date object, or null if invalid.
|
|
7
|
+
*/
|
|
8
|
+
const parseDateFromString = (dateString: string): Date | null => {
|
|
9
|
+
const date = new Date(dateString);
|
|
10
|
+
if (isNaN(date.getTime())) {
|
|
11
|
+
console.warn(
|
|
12
|
+
`Calendar: Invalid date string provided: "${dateString}". Expected format is ISO 8601 (YYYY-MM-DD)`
|
|
13
|
+
);
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
return date;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export default parseDateFromString;
|