@lazerlen/legend-calendar 1.0.0

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.
Files changed (48) hide show
  1. package/.eslintrc.js +29 -0
  2. package/.turbo/turbo-build.log +19 -0
  3. package/.turbo/turbo-lint.log +14 -0
  4. package/.turbo/turbo-test.log +261 -0
  5. package/.turbo/turbo-typecheck.log +1 -0
  6. package/CHANGELOG.md +127 -0
  7. package/dist/index.d.mts +679 -0
  8. package/dist/index.d.ts +679 -0
  9. package/dist/index.js +2 -0
  10. package/dist/index.js.map +1 -0
  11. package/dist/index.mjs +2 -0
  12. package/dist/index.mjs.map +1 -0
  13. package/package.json +47 -0
  14. package/src/components/Calendar.stories.tsx +226 -0
  15. package/src/components/Calendar.tsx +224 -0
  16. package/src/components/CalendarItemDay.tsx +385 -0
  17. package/src/components/CalendarItemEmpty.tsx +30 -0
  18. package/src/components/CalendarItemWeekName.tsx +67 -0
  19. package/src/components/CalendarList.stories.tsx +326 -0
  20. package/src/components/CalendarList.tsx +373 -0
  21. package/src/components/CalendarRowMonth.tsx +62 -0
  22. package/src/components/CalendarRowWeek.tsx +46 -0
  23. package/src/components/CalendarThemeProvider.tsx +43 -0
  24. package/src/components/HStack.tsx +67 -0
  25. package/src/components/VStack.tsx +67 -0
  26. package/src/components/index.ts +108 -0
  27. package/src/developer/decorators.tsx +54 -0
  28. package/src/developer/loggginHandler.tsx +6 -0
  29. package/src/developer/useRenderCount.ts +7 -0
  30. package/src/helpers/dates.test.ts +567 -0
  31. package/src/helpers/dates.ts +163 -0
  32. package/src/helpers/functions.ts +327 -0
  33. package/src/helpers/numbers.ts +11 -0
  34. package/src/helpers/strings.ts +2 -0
  35. package/src/helpers/tokens.ts +71 -0
  36. package/src/helpers/types.ts +3 -0
  37. package/src/hooks/useCalendar.test.ts +381 -0
  38. package/src/hooks/useCalendar.ts +351 -0
  39. package/src/hooks/useCalendarList.test.ts +382 -0
  40. package/src/hooks/useCalendarList.tsx +291 -0
  41. package/src/hooks/useDateRange.test.ts +128 -0
  42. package/src/hooks/useDateRange.ts +94 -0
  43. package/src/hooks/useOptimizedDayMetadata.test.ts +582 -0
  44. package/src/hooks/useOptimizedDayMetadata.ts +93 -0
  45. package/src/hooks/useTheme.ts +14 -0
  46. package/src/index.ts +24 -0
  47. package/tsconfig.json +13 -0
  48. package/tsup.config.ts +15 -0
@@ -0,0 +1,381 @@
1
+ import { describe, expect, it, setSystemTime } from "bun:test";
2
+ import { format } from "date-fns/fp/format";
3
+
4
+ import { buildCalendar } from "@/hooks/useCalendar";
5
+
6
+ describe("buildCalendar", () => {
7
+ it("builds the month row", () => {
8
+ const january = buildCalendar({
9
+ calendarMonthId: "2024-01-15",
10
+ calendarFirstDayOfWeek: "sunday",
11
+ });
12
+ expect(january.calendarRowMonth).toBe("January 2024");
13
+ });
14
+
15
+ it("build the month row with custom formatting", () => {
16
+ const january = buildCalendar({
17
+ calendarMonthId: "2024-01-01",
18
+ calendarFirstDayOfWeek: "sunday",
19
+ getCalendarMonthFormat: format("MMM yy"),
20
+ });
21
+ expect(january.calendarRowMonth).toBe("Jan 24");
22
+ });
23
+
24
+ it("builds the week days row starting on sunday", () => {
25
+ const january = buildCalendar({
26
+ calendarMonthId: "2024-01-01",
27
+ calendarFirstDayOfWeek: "sunday",
28
+ });
29
+
30
+ expect(january.weekDaysList).toEqual(["S", "M", "T", "W", "T", "F", "S"]);
31
+ });
32
+
33
+ it("builds the week days row starting on monday", () => {
34
+ const january = buildCalendar({
35
+ calendarMonthId: "2024-01-01",
36
+ calendarFirstDayOfWeek: "monday",
37
+ });
38
+ expect(january.weekDaysList).toEqual(["M", "T", "W", "T", "F", "S", "S"]);
39
+ });
40
+
41
+ it("builds the weeks row", () => {
42
+ const { weeksList } = buildCalendar({
43
+ calendarMonthId: "2024-02-01",
44
+ calendarFirstDayOfWeek: "sunday",
45
+ });
46
+
47
+ // February 2024 has 5 weeks
48
+ expect(weeksList).toHaveLength(5);
49
+
50
+ // #region First week
51
+ // Every week has 7 days
52
+ expect(weeksList.every((week) => week.length === 7)).toBe(true);
53
+
54
+ let [sunday, monday, tuesday, wednesday, thursday, friday, saturday] =
55
+ weeksList[0];
56
+ expect(sunday.isStartOfWeek).toBe(true);
57
+ expect(saturday.isEndOfWeek).toBe(true);
58
+
59
+ // Filler days are built correctly
60
+ expect(
61
+ [sunday, monday, tuesday, wednesday].every((day) => day.isDifferentMonth)
62
+ ).toBe(true);
63
+ expect(
64
+ [thursday, friday, saturday].every((day) => day.isDifferentMonth)
65
+ ).toBe(false);
66
+
67
+ // Labels are correct
68
+ expect(sunday.displayLabel).toBe("28");
69
+ expect(monday.displayLabel).toBe("29");
70
+ expect(tuesday.displayLabel).toBe("30");
71
+ expect(wednesday.displayLabel).toBe("31");
72
+ expect(thursday.displayLabel).toBe("1");
73
+ expect(friday.displayLabel).toBe("2");
74
+ expect(saturday.displayLabel).toBe("3");
75
+
76
+ // IDs are correct
77
+ expect(sunday.id).toBe("2024-01-28");
78
+ expect(monday.id).toBe("2024-01-29");
79
+ expect(tuesday.id).toBe("2024-01-30");
80
+ expect(wednesday.id).toBe("2024-01-31");
81
+
82
+ expect(thursday.id).toBe("2024-02-01");
83
+ expect(thursday.isStartOfMonth).toBe(true);
84
+
85
+ expect(friday.id).toBe("2024-02-02");
86
+ expect(saturday.id).toBe("2024-02-03");
87
+ // #endregion
88
+
89
+ // #region Last weeek
90
+ [sunday, monday, tuesday, wednesday, thursday, friday, saturday] =
91
+ weeksList[4];
92
+
93
+ // Fillers are built correctly
94
+ expect(sunday.isStartOfWeek).toBe(true);
95
+ expect(saturday.isEndOfWeek).toBe(true);
96
+ expect(
97
+ [sunday, monday, tuesday, wednesday, thursday].every(
98
+ (day) => day.isDifferentMonth
99
+ )
100
+ ).toBe(false);
101
+ expect([friday, saturday].every((day) => day.isDifferentMonth)).toBe(true);
102
+
103
+ // Labels are correct
104
+ expect(sunday.displayLabel).toBe("25");
105
+ expect(monday.displayLabel).toBe("26");
106
+ expect(tuesday.displayLabel).toBe("27");
107
+ expect(wednesday.displayLabel).toBe("28");
108
+ expect(thursday.displayLabel).toBe("29");
109
+ expect(friday.displayLabel).toBe("1");
110
+ expect(saturday.displayLabel).toBe("2");
111
+
112
+ // IDs are correct
113
+ expect(sunday.id).toBe("2024-02-25");
114
+ expect(monday.id).toBe("2024-02-26");
115
+ expect(tuesday.id).toBe("2024-02-27");
116
+ expect(wednesday.id).toBe("2024-02-28");
117
+
118
+ expect(thursday.id).toBe("2024-02-29");
119
+ expect(thursday.isEndOfMonth).toBe(true);
120
+
121
+ expect(friday.id).toBe("2024-03-01");
122
+ expect(saturday.id).toBe("2024-03-02");
123
+ // #endregion
124
+ });
125
+
126
+ it("build the weeks row with custom formatting", () => {
127
+ const { weeksList } = buildCalendar({
128
+ calendarMonthId: "2024-03-01",
129
+ calendarFirstDayOfWeek: "sunday",
130
+ getCalendarDayFormat: format("dd"),
131
+ });
132
+ expect(weeksList[0][6].id).toBe("2024-03-02");
133
+ expect(weeksList[0][6].displayLabel).toBe("02");
134
+ });
135
+
136
+ it("handles the expected range", () => {
137
+ const { weeksList } = buildCalendar({
138
+ calendarMonthId: "2024-02-01",
139
+ calendarFirstDayOfWeek: "sunday",
140
+ calendarActiveDateRanges: [
141
+ { startId: "2024-02-03", endId: "2024-02-05" },
142
+ ],
143
+ });
144
+
145
+ expect(weeksList[0][6].id).toBe("2024-02-03");
146
+ expect(weeksList[0][6].isStartOfRange).toBe(true);
147
+ expect(weeksList[0][6].state).toBe("active");
148
+ expect(weeksList[0][6].isEndOfRange).toBe(false);
149
+
150
+ expect(weeksList[1][1].id).toBe("2024-02-05");
151
+ expect(weeksList[1][1].isEndOfRange).toBe(true);
152
+ expect(weeksList[1][1].state).toBe("active");
153
+ expect(weeksList[1][1].isStartOfRange).toBe(false);
154
+ });
155
+
156
+ it("calendarMinDate", () => {
157
+ const calendarMinDateId = "2024-01-10";
158
+ const february = buildCalendar({
159
+ calendarMonthId: "2024-01-01",
160
+ calendarMinDateId,
161
+ calendarFirstDayOfWeek: "sunday",
162
+ });
163
+
164
+ const days = february.weeksList.flatMap((week) => week.map((day) => day));
165
+ const beforeMinDate = days.filter((day) => day.id < calendarMinDateId);
166
+ const minDateOrAfter = days.filter((day) => day.id >= calendarMinDateId);
167
+
168
+ expect(beforeMinDate.every((day) => day.state === "disabled")).toBe(true);
169
+ expect(minDateOrAfter.every((day) => day.state === "idle")).toBe(true);
170
+ });
171
+
172
+ it("calendarMaxDate", () => {
173
+ const calendarMaxDateId = "2024-02-10";
174
+ const february = buildCalendar({
175
+ calendarMonthId: "2024-02-01",
176
+ calendarMaxDateId,
177
+ calendarFirstDayOfWeek: "sunday",
178
+ });
179
+
180
+ const days = february.weeksList.flatMap((week) => week.map((day) => day));
181
+ const maxDateOrBefore = days.filter((day) => day.id <= calendarMaxDateId);
182
+ const afterMaxDate = days.filter((day) => day.id > calendarMaxDateId);
183
+
184
+ expect(afterMaxDate.every((day) => day.state === "disabled")).toBe(true);
185
+ expect(maxDateOrBefore.every((day) => day.state === "idle")).toBe(true);
186
+ });
187
+
188
+ describe("isLastDayOfWeek", () => {
189
+ it('isLastDayOfWeek respects "sunday" as last day of week', () => {
190
+ const january = buildCalendar({
191
+ calendarMonthId: "2024-01-01",
192
+ calendarFirstDayOfWeek: "sunday",
193
+ });
194
+ const firstWeek = january.weeksList[0];
195
+ const lastDay = firstWeek[firstWeek.length - 1];
196
+ expect(lastDay.id).toBe("2024-01-06");
197
+ expect(lastDay.isEndOfWeek).toBe(true);
198
+ });
199
+
200
+ it('isLastDayOfWeek respects "monday" as last day of week', () => {
201
+ const january = buildCalendar({
202
+ calendarMonthId: "2024-01-01",
203
+ calendarFirstDayOfWeek: "monday",
204
+ });
205
+ const firstWeek = january.weeksList[0];
206
+ const lastDay = firstWeek[firstWeek.length - 1];
207
+ expect(lastDay.id).toBe("2024-01-07");
208
+ expect(lastDay.isEndOfWeek).toBe(true);
209
+ });
210
+ });
211
+
212
+ describe("isWeekend", () => {
213
+ it("starting on sunday", () => {
214
+ const january = buildCalendar({
215
+ calendarMonthId: "2024-01-01",
216
+ calendarFirstDayOfWeek: "sunday",
217
+ });
218
+ const firstWeek = january.weeksList[0];
219
+ const sunday = firstWeek[0];
220
+ const saturday = firstWeek[firstWeek.length - 1];
221
+ const friday = firstWeek[firstWeek.length - 2];
222
+
223
+ expect(friday.isWeekend).toBeFalse();
224
+ expect(sunday.isWeekend).toBeTrue();
225
+ expect(saturday.isWeekend).toBeTrue();
226
+ });
227
+ it("starting on monday", () => {
228
+ const january = buildCalendar({
229
+ calendarMonthId: "2024-01-01",
230
+ calendarFirstDayOfWeek: "monday",
231
+ });
232
+ const firstWeek = january.weeksList[0];
233
+ const sunday = firstWeek[firstWeek.length - 1];
234
+ const saturday = firstWeek[firstWeek.length - 2];
235
+ const friday = firstWeek[firstWeek.length - 3];
236
+
237
+ expect(friday.isWeekend).toBeFalse();
238
+ expect(sunday.isWeekend).toBeTrue();
239
+ expect(saturday.isWeekend).toBeTrue();
240
+ });
241
+ });
242
+
243
+ describe("state", () => {
244
+ it("active: supersedes today", () => {
245
+ // Mock clock
246
+ setSystemTime(new Date(2024, 0 /*January*/, 10));
247
+
248
+ const january = buildCalendar({
249
+ calendarMonthId: "2024-01-01",
250
+ calendarActiveDateRanges: [
251
+ { startId: "2024-01-10", endId: "2024-01-10" },
252
+ ],
253
+ });
254
+
255
+ expect(january.weeksList[1][3].id).toBe("2024-01-10");
256
+ expect(january.weeksList[1][3].isToday).toBeTrue();
257
+
258
+ // Even though it's today, the active state supersedes it
259
+ expect(january.weeksList[1][3].state).toBe("active");
260
+ expect(january.weeksList[1][3].isStartOfRange).toBeTrue();
261
+ expect(january.weeksList[1][3].isEndOfRange).toBeTrue();
262
+
263
+ // Reset clock
264
+ setSystemTime();
265
+ });
266
+
267
+ it("active: supersedes disabled (with calendarDisabledDateIds)", () => {
268
+ const january = buildCalendar({
269
+ calendarMonthId: "2024-01-01",
270
+ calendarActiveDateRanges: [
271
+ { startId: "2024-01-10", endId: "2024-01-10" },
272
+ ],
273
+ calendarDisabledDateIds: ["2024-01-10"],
274
+ });
275
+
276
+ expect(january.weeksList[1][3].id).toBe("2024-01-10");
277
+ expect(january.weeksList[1][3].isDisabled).toBeTrue();
278
+
279
+ // Even though it's disabled, the active state supersedes it
280
+ expect(january.weeksList[1][3].state).toBe("active");
281
+ expect(january.weeksList[1][3].isStartOfRange).toBeTrue();
282
+ expect(january.weeksList[1][3].isEndOfRange).toBeTrue();
283
+ });
284
+
285
+ it("active: supersedes disabled (with min)", () => {
286
+ const january = buildCalendar({
287
+ calendarMonthId: "2024-01-01",
288
+ calendarActiveDateRanges: [
289
+ { startId: "2024-01-10", endId: "2024-01-10" },
290
+ ],
291
+ calendarMinDateId: "2024-01-15",
292
+ });
293
+
294
+ expect(january.weeksList[1][3].id).toBe("2024-01-10");
295
+ expect(january.weeksList[1][3].isDisabled).toBeTrue();
296
+
297
+ // Even though it's disabled, the active state supersedes it
298
+ expect(january.weeksList[1][3].state).toBe("active");
299
+ expect(january.weeksList[1][3].isStartOfRange).toBeTrue();
300
+ expect(january.weeksList[1][3].isEndOfRange).toBeTrue();
301
+ });
302
+
303
+ it("active: supersedes disabled (with max)", () => {
304
+ const january = buildCalendar({
305
+ calendarMonthId: "2024-01-01",
306
+ calendarActiveDateRanges: [
307
+ { startId: "2024-01-10", endId: "2024-01-10" },
308
+ ],
309
+ calendarMaxDateId: "2024-01-09",
310
+ });
311
+
312
+ expect(january.weeksList[1][3].id).toBe("2024-01-10");
313
+ expect(january.weeksList[1][3].isDisabled).toBeTrue();
314
+
315
+ // Even though it's disabled, the active state supersedes it
316
+ expect(january.weeksList[1][3].state).toBe("active");
317
+ expect(january.weeksList[1][3].isStartOfRange).toBeTrue();
318
+ expect(january.weeksList[1][3].isEndOfRange).toBeTrue();
319
+ });
320
+
321
+ it("active: supersedes idle", () => {
322
+ const january = buildCalendar({
323
+ calendarMonthId: "2024-01-01",
324
+ calendarActiveDateRanges: [
325
+ { startId: "2024-01-10", endId: "2024-01-10" },
326
+ ],
327
+ });
328
+
329
+ expect(january.weeksList[1][3].id).toBe("2024-01-10");
330
+
331
+ // Even though it's disabled, the active state supersedes it
332
+ expect(january.weeksList[1][3].state).toBe("active");
333
+ expect(january.weeksList[1][3].isStartOfRange).toBeTrue();
334
+ expect(january.weeksList[1][3].isEndOfRange).toBeTrue();
335
+
336
+ // Just to make sure the idle state is correct
337
+ expect(january.weeksList[1][4].state).toBe("idle");
338
+ });
339
+
340
+ it("disabled: supersedes idle", () => {
341
+ const january = buildCalendar({
342
+ calendarMonthId: "2024-01-01",
343
+ calendarDisabledDateIds: ["2024-01-10"],
344
+ });
345
+
346
+ expect(january.weeksList[1][3].id).toBe("2024-01-10");
347
+ expect(january.weeksList[1][3].isDisabled).toBeTrue();
348
+
349
+ expect(january.weeksList[1][3].state).toBe("disabled");
350
+ // Just to make sure the idle state is correct
351
+ expect(january.weeksList[1][4].state).toBe("idle");
352
+ });
353
+
354
+ it("today: supersedes idle", () => {
355
+ setSystemTime(new Date(2024, 0 /*January*/, 10));
356
+ const january = buildCalendar({
357
+ calendarMonthId: "2024-01-01",
358
+ });
359
+
360
+ expect(january.weeksList[1][3].id).toBe("2024-01-10");
361
+ expect(january.weeksList[1][3].isToday).toBeTrue();
362
+ expect(january.weeksList[1][3].state).toBe("today");
363
+
364
+ // Just to make sure the idle state is correct
365
+ expect(january.weeksList[1][2].state).toBe("idle");
366
+ expect(january.weeksList[1][4].state).toBe("idle");
367
+ setSystemTime();
368
+ });
369
+
370
+ it("idle: serves as the base state", () => {
371
+ const january = buildCalendar({
372
+ calendarMonthId: "2024-01-01",
373
+ });
374
+ expect(
375
+ january.weeksList
376
+ .flatMap((week) => week.map((day) => day.state))
377
+ .every((state) => state === "idle")
378
+ ).toBeTrue();
379
+ });
380
+ });
381
+ });