feat(account page): change test activity graph starting day depending on the browser locale (@fehmer) (#6385)

implements #6356
This commit is contained in:
Christian Fehmer 2025-03-25 12:55:07 +01:00 committed by GitHub
parent 821478e617
commit 978878c180
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 626 additions and 75 deletions

View file

@ -1,6 +1,7 @@
import {
TestActivityCalendar,
ModifiableTestActivityCalendar,
TestActivityDay,
} from "../../src/ts/elements/test-activity-calendar";
import * as Dates from "date-fns";
import { MatcherResult } from "../vitest";
@ -18,7 +19,7 @@ describe("test-activity-calendar.ts", () => {
it("for lastDay in april", () => {
//set today
vi.setSystemTime(getDate("2024-04-10"));
const calendar = new TestActivityCalendar([], getDate("2024-04-10"));
const calendar = new TestActivityCalendar([], getDate("2024-04-10"), 0);
expect(calendar.getMonths()).toEqual([
{
@ -71,11 +72,67 @@ describe("test-activity-calendar.ts", () => {
},
]);
});
it("for lastDay in april start weeks on monday", () => {
//set today
vi.setSystemTime(getDate("2024-04-10"));
const calendar = new TestActivityCalendar([], getDate("2024-04-10"), 1);
expect(calendar.getMonths()).toEqual([
{
text: "apr",
weeks: 3,
},
{
text: "may",
weeks: 5,
},
{
text: "jun",
weeks: 4,
},
{
text: "jul",
weeks: 5,
},
{
text: "aug",
weeks: 4,
},
{
text: "sep",
weeks: 4,
},
{
text: "oct",
weeks: 5,
},
{
text: "nov",
weeks: 4,
},
{
text: "dec",
weeks: 4,
},
{
text: "jan",
weeks: 5,
},
{
text: "feb",
weeks: 4,
},
{
text: "mar",
weeks: 4,
},
]);
});
it("for lastDay in april, not test for the current week", () => {
//set today
vi.setSystemTime(getDate("2024-04-24"));
const calendar = new TestActivityCalendar([], getDate("2024-04-10"));
const calendar = new TestActivityCalendar([], getDate("2024-04-10"), 0);
expect(calendar.getMonths()).toEqual([
{
@ -132,10 +189,71 @@ describe("test-activity-calendar.ts", () => {
},
]);
});
it("for lastDay in april, not test for the current week start weeks on monday", () => {
//set today
vi.setSystemTime(getDate("2024-04-24"));
const calendar = new TestActivityCalendar([], getDate("2024-04-10"), 1);
expect(calendar.getMonths()).toEqual([
{
text: "",
weeks: 1,
},
{
text: "may",
weeks: 5,
},
{
text: "jun",
weeks: 4,
},
{
text: "jul",
weeks: 5,
},
{
text: "aug",
weeks: 4,
},
{
text: "sep",
weeks: 4,
},
{
text: "oct",
weeks: 5,
},
{
text: "nov",
weeks: 4,
},
{
text: "dec",
weeks: 4,
},
{
text: "jan",
weeks: 5,
},
{
text: "feb",
weeks: 4,
},
{
text: "mar",
weeks: 4,
},
{
text: "apr",
weeks: 4,
},
]);
});
it("for lastDay in january", () => {
//set today
vi.setSystemTime(getDate("2023-01-01"));
const calendar = new TestActivityCalendar([], getDate("2023-01-01"));
const calendar = new TestActivityCalendar([], getDate("2023-01-01"), 0);
expect(calendar.getMonths()).toEqual([
{
@ -192,6 +310,7 @@ describe("test-activity-calendar.ts", () => {
const calendar = new TestActivityCalendar(
[],
getDate("2023-05-10"),
0,
true
);
@ -246,10 +365,70 @@ describe("test-activity-calendar.ts", () => {
},
]);
});
it("for lastDay and full year starting with sunday start weeks on monday", () => {
const calendar = new TestActivityCalendar(
[],
getDate("2023-05-10"),
1,
true
);
expect(calendar.getMonths()).toEqual([
{
text: "jan",
weeks: 6,
},
{
text: "feb",
weeks: 4,
},
{
text: "mar",
weeks: 4,
},
{
text: "apr",
weeks: 4,
},
{
text: "may",
weeks: 5,
},
{
text: "jun",
weeks: 4,
},
{
text: "jul",
weeks: 5,
},
{
text: "aug",
weeks: 4,
},
{
text: "sep",
weeks: 4,
},
{
text: "oct",
weeks: 5,
},
{
text: "nov",
weeks: 4,
},
{
text: "dec",
weeks: 4,
},
]);
});
it("for lastDay and full year starting with monday", () => {
const calendar = new TestActivityCalendar(
[],
getDate("2024-05-10"),
0,
true
);
@ -304,10 +483,69 @@ describe("test-activity-calendar.ts", () => {
},
]);
});
it("for lastDay and full year starting with monday start weeks on Monday", () => {
const calendar = new TestActivityCalendar(
[],
getDate("2024-05-10"),
1,
true
);
expect(calendar.getMonths()).toEqual([
{
text: "jan",
weeks: 5,
},
{
text: "feb",
weeks: 4,
},
{
text: "mar",
weeks: 4,
},
{
text: "apr",
weeks: 5,
},
{
text: "may",
weeks: 4,
},
{
text: "jun",
weeks: 4,
},
{
text: "jul",
weeks: 5,
},
{
text: "aug",
weeks: 4,
},
{
text: "sep",
weeks: 5,
},
{
text: "oct",
weeks: 4,
},
{
text: "nov",
weeks: 4,
},
{
text: "dec",
weeks: 5,
},
]);
});
it("for first day in june", () => {
//set today
vi.setSystemTime(getDate("2024-06-01"));
const calendar = new TestActivityCalendar([], getDate("2024-06-01"));
const calendar = new TestActivityCalendar([], getDate("2024-06-01"), 0);
expect(calendar.getMonths()).toEqual([
{
@ -363,7 +601,7 @@ describe("test-activity-calendar.ts", () => {
it("no double month for for 16th june", () => {
//set today
vi.setSystemTime(getDate("2024-06-16"));
const calendar = new TestActivityCalendar([], getDate("2024-06-01"));
const calendar = new TestActivityCalendar([], getDate("2024-06-01"), 0);
expect(calendar.getMonths()).toEqual([
{
@ -426,7 +664,11 @@ describe("test-activity-calendar.ts", () => {
it("for lastDay in april", () => {
const data = getData("2023-04-10", "2024-04-10");
vi.setSystemTime(getDate("2024-04-30"));
const calendar = new TestActivityCalendar(data, getDate("2024-04-10"));
const calendar = new TestActivityCalendar(
data,
getDate("2024-04-10"),
0
);
const days = calendar.getDays();
expect(days).toHaveLength(1 + 366 + 4); //one filler on the start, 366 days in leap year, four fillers at the end
@ -454,13 +696,59 @@ describe("test-activity-calendar.ts", () => {
for (let day = 347; day <= 366; day++) {
expect(days[day]).toHaveLevel(0);
}
for (let day = 367; day <= 370; day++) {
expect(days[day]).toBeFiller();
}
});
it("for lastDay in april start weeks on Monday", () => {
const data = getData("2023-04-10", "2024-04-10");
vi.setSystemTime(getDate("2024-04-30"));
const calendar = new TestActivityCalendar(
data,
getDate("2024-04-10"),
1
);
const days = calendar.getDays();
expect(days).toHaveLength(1 + 366 + 4); //one filler on the start, 366 days in leap year, four fillers at the end
//may 23 starts with a monday
expect(days[0]).toBeDate("2023-05-01").toHaveTests(121);
expect(days[1]).toBeDate("2023-05-02").toHaveTests(122).toHaveLevel(2);
expect(days[244])
.toBeDate("2023-12-31")
.toHaveTests(365)
.toHaveLevel(4);
expect(days[245]).toBeDate("2024-01-01").toHaveTests(1).toHaveLevel(1);
expect(days[304]).toBeDate("2024-02-29").toHaveTests(60).toHaveLevel(1);
expect(days[345])
.toBeDate("2024-04-10")
.toHaveTests(101)
.toHaveLevel(2);
//days from April 11th to April 30th
for (let day = 346; day <= 365; day++) {
expect(days[day]).toHaveLevel(0);
}
for (let day = 366; day <= 370; day++) {
expect(days[day]).toBeFiller();
}
});
it("for full leap year", () => {
//GIVEN
const data = getData("2024-01-01", "2024-12-31");
vi.setSystemTime(getDate("2024-12-31"));
const calendar = new TestActivityCalendar(data, getDate("2024-12-31"));
const calendar = new TestActivityCalendar(
data,
getDate("2024-12-31"),
0
);
//WHEN
const days = calendar.getDays();
@ -484,13 +772,46 @@ describe("test-activity-calendar.ts", () => {
}
});
it("for full leap year start weeks on Monday", () => {
//GIVEN
const data = getData("2024-01-01", "2024-12-31");
vi.setSystemTime(getDate("2024-12-31"));
const calendar = new TestActivityCalendar(
data,
getDate("2024-12-31"),
1
);
//WHEN
const days = calendar.getDays();
//THEN
expect(days).toHaveLength(1 + 366 + 4); //one filler on the start, 366 days in leap year, four fillers at the end
//2024 starts with a monday
expect(days[0]).toBeDate("2024-01-01");
expect(days[1]).toBeDate("2024-01-02").toHaveTests(2).toHaveLevel(1);
expect(days[59]).toBeDate("2024-02-29").toHaveTests(60).toHaveLevel(1);
expect(days[365])
.toBeDate("2024-12-31")
.toHaveTests(366)
.toHaveLevel(4);
//2024 ends with a thuesday
for (let day = 366; day < 1 + 366 + 4; day++) {
expect(days[day]).toBeFiller();
}
});
it("for full year", () => {
//GIVEN
const data = getData("2022-11-30", "2023-12-31");
vi.setSystemTime(getDate("2023-12-31"));
const calendar = new TestActivityCalendar(
data,
new Date("2023-12-31T23:59:59Z")
new Date("2023-12-31T23:59:59Z"),
0
); //2023-12-31T23:59:59Z
//WHEN
@ -524,7 +845,11 @@ describe("test-activity-calendar.ts", () => {
//GIVEN
const data = getData("2023-03-28", "2024-04-10"); //extra data in front of the calendar
vi.setSystemTime(getDate("2024-04-30"));
const calendar = new TestActivityCalendar(data, getDate("2024-04-10"));
const calendar = new TestActivityCalendar(
data,
getDate("2024-04-10"),
0
);
//WHEN
const days = calendar.getDays();
@ -546,7 +871,11 @@ describe("test-activity-calendar.ts", () => {
//GIVEN
const data = getData("2024-04-01", "2024-04-10");
vi.setSystemTime(getDate("2024-04-30"));
const calendar = new TestActivityCalendar(data, getDate("2024-04-10"));
const calendar = new TestActivityCalendar(
data,
getDate("2024-04-10"),
0
);
//WHEN
const days = calendar.getDays();
@ -574,7 +903,11 @@ describe("test-activity-calendar.ts", () => {
//GIVEN
const data = getData("2022-02-10", "2023-02-10");
vi.setSystemTime(getDate("2023-02-28"));
const calendar = new TestActivityCalendar(data, getDate("2023-02-10"));
const calendar = new TestActivityCalendar(
data,
getDate("2023-02-10"),
0
);
//WHEN
const days = calendar.getDays();
@ -608,7 +941,11 @@ describe("test-activity-calendar.ts", () => {
//GIVEN
const data = getData("2022-02-10", "2023-02-10");
vi.setSystemTime(getDate("2023-02-12"));
const calendar = new TestActivityCalendar(data, getDate("2023-02-10"));
const calendar = new TestActivityCalendar(
data,
getDate("2023-02-10"),
0
);
//WHEN
const days = calendar.getDays();
@ -639,6 +976,7 @@ describe("test-activity-calendar.ts", () => {
const calendar = new TestActivityCalendar(
data,
getDate("2024-02-10"),
0,
true
);
@ -657,10 +995,38 @@ describe("test-activity-calendar.ts", () => {
expect(days[day]).toHaveLevel(0);
}
//december 24 ends with a tuesday
expect(days[367]).toBeFiller();
expect(days[368]).toBeFiller();
expect(days[369]).toBeFiller();
expect(days[370]).toBeFiller();
for (let day = 367; day <= 370; day++) {
expect(days[day]).toBeFiller();
}
});
it("for lastDay in february full year start weeks on Monday", () => {
//GIVEN
const data = getData("2023-02-10", "2024-02-10");
const calendar = new TestActivityCalendar(
data,
getDate("2024-02-10"),
1,
true
);
//WHEN
const days = calendar.getDays();
//THEN
//january 24 starts with a monday,
expect(days[0]).toBeDate("2024-01-01").toHaveTests(1).toHaveLevel(1);
expect(days[1]).toBeDate("2024-01-02").toHaveTests(2).toHaveLevel(1);
expect(days[40]).toBeDate("2024-02-10").toHaveTests(41).toHaveLevel(4);
//days from 11th february to 31th december
for (let day = 41; day <= 365; day++) {
expect(days[day]).toHaveLevel(0);
}
//december 24 ends with a tuesday
for (let day = 366; day <= 370; day++) {
expect(days[day]).toBeFiller();
}
});
});
});
@ -672,7 +1038,8 @@ describe("test-activity-calendar.ts", () => {
vi.setSystemTime(getDate("2024-04-30"));
const calendar = new ModifiableTestActivityCalendar(
[1, 2, 3],
lastDate
lastDate,
0
);
//WHEN
@ -693,7 +1060,8 @@ describe("test-activity-calendar.ts", () => {
vi.setSystemTime(getDate("2024-04-10"));
const calendar = new ModifiableTestActivityCalendar(
[1, 2, 3],
lastDate
lastDate,
0
);
//WHEN
@ -730,7 +1098,8 @@ describe("test-activity-calendar.ts", () => {
vi.setSystemTime(getDate("2024-04-10"));
const calendar = new ModifiableTestActivityCalendar(
[1, 2, 3],
getDate("2024-04-10")
getDate("2024-04-10"),
0
);
//WHEN
@ -754,7 +1123,8 @@ describe("test-activity-calendar.ts", () => {
vi.setSystemTime(getDate("2024-12-24"));
const calendar = new ModifiableTestActivityCalendar(
getData("2023-12-20", "2024-12-24"),
getDate("2024-12-24")
getDate("2024-12-24"),
0
);
//WHEN
@ -774,7 +1144,8 @@ describe("test-activity-calendar.ts", () => {
//GIVEN
const calendar = new ModifiableTestActivityCalendar(
[1, 2, 3],
getDate("2024-04-10")
getDate("2024-04-10"),
0
);
//WHEN
@ -793,7 +1164,8 @@ describe("test-activity-calendar.ts", () => {
const lastDate = getDate("2024-01-02");
const calendar = new ModifiableTestActivityCalendar(
[1, 2, 3, 4],
lastDate
lastDate,
0
);
//WHEN
@ -825,7 +1197,8 @@ describe("test-activity-calendar.ts", () => {
const lastDate = getDate("2024-01-02");
const calendar = new ModifiableTestActivityCalendar(
[1, 2, 3, 4],
lastDate
lastDate,
0
);
//THEN
@ -852,10 +1225,7 @@ function getData(from: string, to: string): number[] {
}
expect.extend({
toBeDate(
received: MonkeyTypes.TestActivityDay,
expected: string
): MatcherResult {
toBeDate(received: TestActivityDay, expected: string): MatcherResult {
const expectedDate = Dates.format(getDate(expected), "EEEE dd MMM yyyy");
const actual = received.label?.substring(received.label.indexOf("on") + 3);
@ -866,10 +1236,7 @@ expect.extend({
expected: expectedDate,
};
},
toHaveTests(
received: MonkeyTypes.TestActivityDay,
expected: number
): MatcherResult {
toHaveTests(received: TestActivityDay, expected: number): MatcherResult {
const expectedLabel = `${expected} ${expected == 1 ? "test" : "tests"}`;
const actual = received.label?.substring(0, received.label.indexOf(" on"));
@ -881,7 +1248,7 @@ expect.extend({
};
},
toHaveLevel(
received: MonkeyTypes.TestActivityDay,
received: TestActivityDay,
expected: string | number
): MatcherResult {
return {
@ -892,7 +1259,7 @@ expect.extend({
};
},
toBeFiller(received: MonkeyTypes.TestActivityDay): MatcherResult {
toBeFiller(received: TestActivityDay): MatcherResult {
return {
pass: received.level === "filler",
message: () => `Is not a filler.`,

View file

@ -0,0 +1,87 @@
import * as DateAndTime from "../../src/ts/utils/date-and-time";
describe("date-and-time", () => {
const testCases = [
{ locale: "en-US", firstDayOfWeek: 0 },
{ locale: "en", firstDayOfWeek: 0 },
{ locale: "de-DE", firstDayOfWeek: 1 },
{ locale: "en-DE", firstDayOfWeek: 1, firefoxFirstDayOfWeek: 0 },
{ locale: "de-AT", firstDayOfWeek: 1 },
{ locale: "ps-AF", firstDayOfWeek: 6, firefoxFirstDayOfWeek: 0 },
{ locale: "de-unknown", firstDayOfWeek: 1 },
{ locale: "xx-yy", firstDayOfWeek: 1, firefoxFirstDayOfWeek: 0 },
];
describe("getFirstDayOfTheWeek", () => {
const languageMock = vi.spyOn(window.navigator, "language", "get");
const localeMock = vi.spyOn(Intl, "Locale");
beforeEach(() => {
languageMock.mockReset();
localeMock.mockReset();
});
it("fallback to sunday for missing language", () => {
//GIVEN
languageMock.mockReturnValue(null as any);
//WHEN / THEN
expect(DateAndTime.getFirstDayOfTheWeek()).toEqual(0);
});
describe("with weekInfo", () => {
it.for(testCases)(`$locale`, ({ locale, firstDayOfWeek }) => {
//GIVEN
languageMock.mockReturnValue(locale);
localeMock.mockImplementationOnce(
() => ({ weekInfo: { firstDay: firstDayOfWeek } } as any)
);
//WHEN/THEN
expect(DateAndTime.getFirstDayOfTheWeek()).toEqual(firstDayOfWeek);
});
});
describe("with getWeekInfo", () => {
it("with getWeekInfo on monday", () => {
languageMock.mockReturnValue("en-US");
localeMock.mockImplementationOnce(
() => ({ getWeekInfo: () => ({ firstDay: 1 }) } as any)
);
//WHEN/THEN
expect(DateAndTime.getFirstDayOfTheWeek()).toEqual(1);
});
it("with getWeekInfo on sunday", () => {
languageMock.mockReturnValue("en-US");
localeMock.mockImplementationOnce(
() => ({ getWeekInfo: () => ({ firstDay: 7 }) } as any)
);
//WHEN/THEN
expect(DateAndTime.getFirstDayOfTheWeek()).toEqual(0);
});
});
describe("without weekInfo (firefox)", () => {
beforeEach(() => {
localeMock.mockImplementationOnce(() => ({} as any));
});
it.for(testCases)(
`$locale`,
({ locale, firstDayOfWeek, firefoxFirstDayOfWeek }) => {
//GIVEN
languageMock.mockReturnValue(locale);
//WHEN/THEN
expect(DateAndTime.getFirstDayOfTheWeek()).toEqual(
firefoxFirstDayOfWeek !== undefined
? firefoxFirstDayOfWeek
: firstDayOfWeek
);
}
);
});
});
});

View file

@ -1,6 +1,7 @@
import type { Assertion, AsymmetricMatchersContaining } from "vitest";
import { TestActivityDay } from "../src/ts/elements/test-activity-calendar";
interface ActivityDayMatchers<R = MonkeyTypes.TestActivityDay> {
interface ActivityDayMatchers<R = TestActivityDay> {
toBeDate: (date: string) => ActivityDayMatchers<R>;
toHaveTests: (tests: number) => ActivityDayMatchers<R>;
toHaveLevel: (level?: string | number) => ActivityDayMatchers<R>;

View file

@ -309,24 +309,8 @@
</div>
<div class="activity"></div>
<div class="months"></div>
<div class="daysFull">
<div></div>
<div><div class="text">monday</div></div>
<div></div>
<div><div class="text">wednesday</div></div>
<div></div>
<div><div class="text">friday</div></div>
<div></div>
</div>
<div class="days">
<div></div>
<div><div class="text">mon</div></div>
<div></div>
<div><div class="text">wed</div></div>
<div></div>
<div><div class="text">fri</div></div>
<div></div>
</div>
<div class="daysFull"></div>
<div class="days"></div>
<div class="nodata hidden">No data found.</div>
</div>
</div>

View file

@ -522,10 +522,6 @@
font-size: var(--font-size);
}
.days {
display: none;
}
.daysFull {
margin-right: 2rem;
}
@ -551,6 +547,10 @@
}
}
.days {
display: none;
}
.nodata {
grid-area: chart;
}

View file

@ -29,8 +29,10 @@ import {
} from "./constants/default-snapshot";
import { getDefaultConfig } from "./constants/default-config";
import { FunboxMetadata } from "../../../packages/funbox/src/types";
import { getFirstDayOfTheWeek } from "./utils/date-and-time";
let dbSnapshot: Snapshot | undefined;
const firstDayOfTheWeek = getFirstDayOfTheWeek();
export class SnapshotInitError extends Error {
constructor(message: string, public responseCode: number) {
@ -163,7 +165,8 @@ export async function initSnapshot(): Promise<Snapshot | number | boolean> {
if (userData.testActivity !== undefined) {
snap.testActivity = new ModifiableTestActivityCalendar(
userData.testActivity.testsByDays,
new Date(userData.testActivity.lastDay)
new Date(userData.testActivity.lastDay),
firstDayOfTheWeek
);
}
@ -1055,6 +1058,7 @@ export async function getTestActivityCalendar(
dbSnapshot.testActivityByYear[year] = new TestActivityCalendar(
testsByDays,
lastDay,
firstDayOfTheWeek,
true
);
}

View file

@ -11,16 +11,15 @@ import {
startOfYear,
differenceInWeeks,
startOfMonth,
nextSunday,
previousSunday,
isSunday,
nextSaturday,
isSaturday,
subWeeks,
Interval,
toDate,
previousDay,
Day,
nextDay,
} from "date-fns";
type TestActivityDay = {
export type TestActivityDay = {
level: string;
label?: string;
};
@ -35,12 +34,15 @@ export class TestActivityCalendar implements TestActivityCalendar {
protected startDay: Date;
protected endDay: Date;
protected isFullYear: boolean;
public firstDayOfWeek: Day;
constructor(
data: (number | null | undefined)[],
lastDay: Date,
firstDayOfWeek: Day,
fullYear = false
) {
this.firstDayOfWeek = firstDayOfWeek;
const local = new UTCDateMini(lastDay);
const interval = this.getInterval(local, fullYear);
@ -56,7 +58,9 @@ export class TestActivityCalendar implements TestActivityCalendar {
if (!fullYear) {
//show the last 52 weeks. Not using one year to avoid the graph to show 54 weeks
start = addDays(subWeeks(end, 52), 1);
if (!isSunday(start)) start = previousSunday(start);
if (!this.isFirstDayOfWeek(start)) {
start = this.previousFirstDayOfWeek(start);
}
}
return { start, end };
@ -91,11 +95,14 @@ export class TestActivityCalendar implements TestActivityCalendar {
let start = i === 0 ? this.startDay : startOfMonth(month);
let end = i === months.length - 1 ? this.endDay : endOfMonth(start);
if (!isSunday(start)) {
start = (i === 0 ? previousSunday : nextSunday)(start);
if (!this.isFirstDayOfWeek(start)) {
start =
i === 0
? this.previousFirstDayOfWeek(start)
: this.nextFirstDayOfWeek(start);
}
if (!isSaturday(end)) {
end = nextSaturday(end);
if (!this.isLastDayOfWeek(end)) {
end = this.nextLastDayOfWeek(end);
}
const weeks = differenceInWeeks(end, start, { roundingMethod: "ceil" });
@ -124,7 +131,7 @@ export class TestActivityCalendar implements TestActivityCalendar {
};
//skip weekdays in the previous month
for (let i = 0; i < this.startDay.getDay(); i++) {
for (let i = 0; i < this.startDay.getDay() - this.firstDayOfWeek; i++) {
result.push({
level: "filler",
});
@ -148,7 +155,7 @@ export class TestActivityCalendar implements TestActivityCalendar {
}
//add weekdays missing
for (let i = this.endDay.getDay(); i < 6; i++) {
for (let i = this.endDay.getDay() - this.firstDayOfWeek; i < 6; i++) {
result.push({
level: "filler",
});
@ -178,6 +185,22 @@ export class TestActivityCalendar implements TestActivityCalendar {
const mid = sum / trimmed.length;
return [Math.floor(mid / 2), Math.round(mid), Math.round(mid * 1.5)];
}
private isFirstDayOfWeek(date: Date): boolean {
return toDate(date).getDay() === this.firstDayOfWeek;
}
private previousFirstDayOfWeek(date: Date): Date {
return previousDay(date, this.firstDayOfWeek);
}
private nextFirstDayOfWeek(date: Date): Date {
return nextDay(date, this.firstDayOfWeek);
}
private isLastDayOfWeek(date: Date): boolean {
return toDate(date).getDay() === (this.firstDayOfWeek + 6) % 7;
}
private nextLastDayOfWeek(date: Date): Date {
return nextDay(date, ((this.firstDayOfWeek + 6) % 7) as Day);
}
}
export class ModifiableTestActivityCalendar
@ -186,8 +209,8 @@ export class ModifiableTestActivityCalendar
{
private lastDay: Date;
constructor(data: (number | null)[], lastDay: Date) {
super(data, lastDay);
constructor(data: (number | null)[], lastDay: Date, firstDayOfWeek: Day) {
super(data, lastDay, firstDayOfWeek);
this.lastDay = new UTCDateMini(lastDay);
}
@ -218,9 +241,14 @@ export class ModifiableTestActivityCalendar
getFullYearCalendar(): TestActivityCalendar {
const today = new Date();
if (this.lastDay.getFullYear() !== new UTCDateMini(today).getFullYear()) {
return new TestActivityCalendar([], today, true);
return new TestActivityCalendar([], today, this.firstDayOfWeek, true);
} else {
return new TestActivityCalendar(this.data, this.lastDay, true);
return new TestActivityCalendar(
this.data,
this.lastDay,
this.firstDayOfWeek,
true
);
}
}
}

View file

@ -22,6 +22,7 @@ export function init(
yearSelector = getYearSelector();
initYearSelector("current", userSignUpDate?.getFullYear() || 2022);
updateLabels(calendar.firstDayOfWeek);
update(calendar);
}
@ -128,3 +129,40 @@ function getYearSelector(): SlimSelect {
});
return yearSelector;
}
const daysDisplay = [
"sunday",
"monday",
"tuesday",
"wednesday",
"thursday",
"friday",
"saturday",
];
function updateLabels(firstDayOfWeek: number): void {
const days: (string | undefined)[] = [];
for (let i = 0; i < 7; i++) {
days.push(
i % 2 != firstDayOfWeek % 2
? daysDisplay[(firstDayOfWeek + i) % 7]
: undefined
);
}
const buildHtml = (maxLength?: number): string => {
const shorten =
maxLength !== undefined
? (it: string) => it.substring(0, maxLength)
: (it: string) => it;
return days
.map((it) =>
it !== undefined
? `<div><div class="text">${shorten(it)}</div></div>`
: "<div></div>"
)
.join("");
};
$("#testActivity .daysFull").html(buildHtml());
$("#testActivity .days").html(buildHtml(3));
}

View file

@ -1,5 +1,6 @@
import { roundTo2 } from "@monkeytype/util/numbers";
import { Day } from "date-fns";
import * as Locales from "date-fns/locale";
/**
* Converts seconds to a human-readable string representation of time.
* @param sec The number of seconds to convert.
@ -104,3 +105,44 @@ export function secondsToString(
}
return ret.trim();
}
export function getFirstDayOfTheWeek(): Day {
if (navigator.language === undefined || navigator.language === null) {
return 0;
}
const locale = new Intl.Locale(navigator.language);
if (locale === undefined || locale === null) {
return 0; //sunday
}
//modern browsers support `weekInfo` or `getWeekInfo()`
if ("weekInfo" in locale) {
// @ts-ignore
return (locale.weekInfo.firstDay as number) % 7;
}
if ("getWeekInfo" in locale) {
// @ts-ignore
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
return (locale.getWeekInfo().firstDay as number) % 7;
}
//use date-fns for browsers like firefox
// @ts-ignore
let dateFnsLocale = Locales[
navigator.language.replaceAll("-", "")
] as Locales.Locale;
if (dateFnsLocale === undefined || dateFnsLocale === null) {
//retry with language only
// @ts-ignore
dateFnsLocale = Locales[navigator.language.split("-")[0]] as Locales.Locale;
}
if (dateFnsLocale !== undefined && dateFnsLocale !== null) {
return ((dateFnsLocale.options?.weekStartsOn ?? 0) % 7) as Day;
}
return 0; //start on sunday
}