[#106] added prettier check to jenkins and prettified files (#109)

Co-authored-by: Camille Moussu <cmoussu@linagora.com>
This commit is contained in:
Camille Moussu
2025-09-12 11:05:52 +02:00
committed by GitHub
parent 3b87965bed
commit d76775c7d2
16 changed files with 108 additions and 84 deletions
Vendored
+5
View File
@@ -26,6 +26,11 @@ pipeline {
sh 'npm run test' sh 'npm run test'
} }
} }
stage('Check Formatting') {
steps {
sh 'npx prettier --check .'
}
}
stage('Deploy docker images') { stage('Deploy docker images') {
when { when {
anyOf { anyOf {
+17 -4
View File
@@ -8,11 +8,20 @@ This project aims at service a Single Page Application allowing a user to intera
This frontend-only application will interact with: This frontend-only application will interact with:
- [esn-sabre](https://github.com/linagora/esn-sabre/) CalDAV + CardDAV server, tailor made for LINAGORA needs. - [esn-sabre](https://github.com/linagora/esn-sabre/) CalDAV + CardDAV server, tailor made for LINAGORA needs.
- [Twake Calendar side service](https://github.com/linagora/twake-calendar-side-service) that delivers additional backend features for Sabre. - [Twake Calendar side service](https://github.com/linagora/twake-calendar-side-service) that delivers additional backend features for Sabre.
It is meant as a drop in replacement of [esn-frontend-calendar](https://github.com/linagora/esn-frontend-calendar). It is meant as a drop in replacement of [esn-frontend-calendar](https://github.com/linagora/esn-frontend-calendar).
## Contributing
### Formating
We use [Prettier](https://prettier.io/) to keep code style consistent.
A `.prettierrc` file is already included in the repo, so formatting rules are predefined.
Before committing, make sure you format your files either using your IDE Prettier extension or Prettier CLI.
## Running it ## Running it
Requirement: node 24+ Requirement: node 24+
@@ -77,7 +86,7 @@ And then visit [https://localhost:5000](https://localhost:5000).
### App grid ### App grid
An applist is configurable in the public folder to setup the grid of app accessible within Twake Calendar. An applist is configurable in the public folder to setup the grid of app accessible within Twake Calendar.
Each app has three fields: Each app has three fields:
@@ -89,7 +98,11 @@ Example:
```js ```js
var appList = [ var appList = [
{ name: "My App", icon: "https://myapp.com/myapp.png", link: "https://myapp.com" }, {
name: "My App",
icon: "https://myapp.com/myapp.png",
link: "https://myapp.com",
},
]; ];
``` ```
@@ -111,7 +111,7 @@ describe("CalendarApp integration", () => {
openpaasId: "667037022b752d0026472254", openpaasId: "667037022b752d0026472254",
}, },
tokens: { tokens: {
accessToken: "token" accessToken: "token",
}, },
}, },
calendars: { calendars: {
@@ -133,14 +133,16 @@ describe("CalendarApp integration", () => {
cn: "Alice", cn: "Alice",
cal_address: "alice@example.com", cal_address: "alice@example.com",
}, },
attendee: [{ attendee: [
cn: "Alice", {
partstat: "ACCEPTED", cn: "Alice",
rsvp: "TRUE", partstat: "ACCEPTED",
role: "REQ-PARTICIPANT", rsvp: "TRUE",
cutype: "INDIVIDUAL", role: "REQ-PARTICIPANT",
cal_address: "alice@example.com", cutype: "INDIVIDUAL",
}, ], cal_address: "alice@example.com",
},
],
...eventProps, ...eventProps,
}, },
}, },
+21 -20
View File
@@ -57,33 +57,34 @@ describe("MiniCalendar", () => {
}); });
it.each([ it.each([
{ today: new Date(2025, 8, 1), label: "Monday 1 Sept 2025" }, { today: new Date(2025, 8, 1), label: "Monday 1 Sept 2025" },
{ today: new Date(2025, 8, 3), label: "Wednesday 3 Sept 2025" }, { today: new Date(2025, 8, 3), label: "Wednesday 3 Sept 2025" },
{ today: new Date(2025, 8, 7), label: "Sunday 7 Sept 2025" }, { today: new Date(2025, 8, 7), label: "Sunday 7 Sept 2025" },
])( ])(
"renders mini calendar with the week in gray (except for today) when today is $label", "renders mini calendar with the week in gray (except for today) when today is $label",
({ today }) => { ({ today }) => {
jest.useFakeTimers(); jest.useFakeTimers();
jest.setSystemTime(today); jest.setSystemTime(today);
renderCalendar(); renderCalendar();
const monday = new Date(2025, 8, 1); // Monday Sept 1 const monday = new Date(2025, 8, 1); // Monday Sept 1
for (let i = 0; i < 7; i++) { for (let i = 0; i < 7; i++) {
const date = new Date(monday); const date = new Date(monday);
date.setDate(monday.getDate() + i); date.setDate(monday.getDate() + i);
const dateTestId = `date-${date.getFullYear()}-${date.getMonth()}-${date.getDate()}`; const dateTestId = `date-${date.getFullYear()}-${date.getMonth()}-${date.getDate()}`;
const tile = screen.getByTestId(dateTestId);
if (date.getTime() !== today.setHours(0, 0, 0, 0)) { const tile = screen.getByTestId(dateTestId);
expect(tile?.parentElement).toHaveClass("selectedWeek");
if (date.getTime() !== today.setHours(0, 0, 0, 0)) {
expect(tile?.parentElement).toHaveClass("selectedWeek");
}
} }
}
jest.useRealTimers(); jest.useRealTimers();
}); }
);
it("renders mini calendar with the day in gray (except for today) when full calendar in day view", async () => { it("renders mini calendar with the day in gray (except for today) when full calendar in day view", async () => {
renderCalendar(); renderCalendar();
@@ -95,15 +95,13 @@ describe("Event Preview Display", () => {
}; };
it("renders correctly event data", () => { it("renders correctly event data", () => {
jest jest.spyOn(Date.prototype, "toLocaleString").mockImplementation(function (
.spyOn(Date.prototype, "toLocaleString") this: Date,
.mockImplementation(function ( locales?: Intl.LocalesArgument,
this: Date, options?: Intl.DateTimeFormatOptions | undefined
locales?: Intl.LocalesArgument, ): string {
options?: Intl.DateTimeFormatOptions | undefined return RealDateToLocaleString.call(this, "en-UK", options);
): string { });
return RealDateToLocaleString.call(this, "en-UK", options);
});
renderWithProviders( renderWithProviders(
<EventPreviewModal <EventPreviewModal
anchorPosition={{ top: 0, left: 0 }} anchorPosition={{ top: 0, left: 0 }}
+3 -3
View File
@@ -1,6 +1,6 @@
module.exports = { module.exports = {
presets: [ presets: [
['@babel/preset-env', { targets: { node: 'current' } }], ["@babel/preset-env", { targets: { node: "current" } }],
['@babel/preset-react', { runtime: 'automatic', importSource: 'preact' }], ["@babel/preset-react", { runtime: "automatic", importSource: "preact" }],
], ],
}; };
+1 -1
View File
@@ -6,4 +6,4 @@ var SSO_RESPONSE_TYPE = "code";
var SSO_CODE_CHALLENGE_METHOD = "S256"; var SSO_CODE_CHALLENGE_METHOD = "S256";
var SSO_POST_LOGOUT_REDIRECT = "http://example.com?logout=1"; var SSO_POST_LOGOUT_REDIRECT = "http://example.com?logout=1";
var CALENDAR_BASE_URL = "https://calendar.example.com"; var CALENDAR_BASE_URL = "https://calendar.example.com";
var MAIL_SPA_URL = "https://mail.example.com" var MAIL_SPA_URL = "https://mail.example.com";
+1 -1
View File
@@ -1,4 +1,4 @@
<!DOCTYPE html> <!doctype html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
+8 -2
View File
@@ -1,5 +1,11 @@
declare namespace Intl { declare namespace Intl {
type Key = 'calendar' | 'collation' | 'currency' | 'numberingSystem' | 'timeZone' | 'unit'; type Key =
| "calendar"
| "collation"
| "currency"
| "numberingSystem"
| "timeZone"
| "unit";
function supportedValuesOf(input: Key): string[]; function supportedValuesOf(input: Key): string[];
} }
+4 -4
View File
@@ -1,5 +1,5 @@
import { useDispatch, useSelector } from 'react-redux' import { useDispatch, useSelector } from "react-redux";
import type { RootState, AppDispatch } from './store' import type { RootState, AppDispatch } from "./store";
export const useAppDispatch = useDispatch.withTypes<AppDispatch>() export const useAppDispatch = useDispatch.withTypes<AppDispatch>();
export const useAppSelector = useSelector.withTypes<RootState>() export const useAppSelector = useSelector.withTypes<RootState>();
+3 -1
View File
@@ -133,7 +133,9 @@ main {
/* selected day hover */ /* selected day hover */
.react-calendar__tile { .react-calendar__tile {
transition: background-color 0.3s ease, color 0.3s ease; transition:
background-color 0.3s ease,
color 0.3s ease;
border: none; border: none;
background-color: white; background-color: white;
width: 12px; width: 12px;
+16 -7
View File
@@ -31,13 +31,13 @@ import { createSelector } from "@reduxjs/toolkit";
import HelpOutlineIcon from "@mui/icons-material/HelpOutline"; import HelpOutlineIcon from "@mui/icons-material/HelpOutline";
import ClearIcon from "@mui/icons-material/Clear"; import ClearIcon from "@mui/icons-material/Clear";
import AccessTimeIcon from "@mui/icons-material/AccessTime"; import AccessTimeIcon from "@mui/icons-material/AccessTime";
import LockIcon from '@mui/icons-material/Lock'; import LockIcon from "@mui/icons-material/Lock";
import { userAttendee } from "../../features/User/userDataTypes"; import { userAttendee } from "../../features/User/userDataTypes";
const computeStartOfTheWeek = (date: Date): Date => { const computeStartOfTheWeek = (date: Date): Date => {
const startOfWeek = new Date(date); const startOfWeek = new Date(date);
startOfWeek.setDate(date.getDate() - ((date.getDay() + 6) % 7)); // Monday startOfWeek.setDate(date.getDate() - ((date.getDay() + 6) % 7)); // Monday
startOfWeek.setHours(0, 0, 0, 0); startOfWeek.setHours(0, 0, 0, 0);
return startOfWeek; return startOfWeek;
}; };
@@ -204,7 +204,6 @@ export default function CalendarApp() {
calendarRef.current?.view.type === "timeGridWeek" || calendarRef.current?.view.type === "timeGridWeek" ||
calendarRef.current?.view.type === undefined calendarRef.current?.view.type === undefined
) { ) {
const startOfWeek = computeStartOfTheWeek(selected); const startOfWeek = computeStartOfTheWeek(selected);
const endOfWeek = new Date(startOfWeek); const endOfWeek = new Date(startOfWeek);
@@ -430,7 +429,9 @@ export default function CalendarApp() {
if (!calendars[arg.event._def.extendedProps.calId]) return; if (!calendars[arg.event._def.extendedProps.calId]) return;
const attendees = event._def.extendedProps.attendee || []; const attendees = event._def.extendedProps.attendee || [];
const isPrivate = event._def.extendedProps.class === "PRIVATE" || event._def.extendedProps.class === "CONFIDENTIAL"; const isPrivate =
event._def.extendedProps.class === "PRIVATE" ||
event._def.extendedProps.class === "CONFIDENTIAL";
let Icon = null; let Icon = null;
let titleStyle: React.CSSProperties = {}; let titleStyle: React.CSSProperties = {};
const ownerEmails = new Set( const ownerEmails = new Set(
@@ -461,9 +462,17 @@ export default function CalendarApp() {
} }
return ( return (
<div style={{ display: 'flex', alignItems: 'center' }}> <div style={{ display: "flex", alignItems: "center" }}>
{isPrivate && <LockIcon data-testid="lock-icon" fontSize="small" style={{ marginRight: '4px' }} />} {isPrivate && (
{Icon && <Icon fontSize="small" style={{ marginRight: '4px' }}/>} <LockIcon
data-testid="lock-icon"
fontSize="small"
style={{ marginRight: "4px" }}
/>
)}
{Icon && (
<Icon fontSize="small" style={{ marginRight: "4px" }} />
)}
<span style={titleStyle}>{event.title}</span> <span style={titleStyle}>{event.title}</span>
</div> </div>
); );
+5 -2
View File
@@ -28,7 +28,6 @@ function CalendarPopover({
const [timeZone, setTimeZone] = useState(""); const [timeZone, setTimeZone] = useState("");
const timezones = Intl.supportedValuesOf?.("timeZone") ?? []; const timezones = Intl.supportedValuesOf?.("timeZone") ?? [];
const handleSave = () => { const handleSave = () => {
dispatch(createCalendar({ name, description, color })); dispatch(createCalendar({ name, description, color }));
onClose({}, "backdropClick"); onClose({}, "backdropClick");
@@ -64,7 +63,11 @@ function CalendarPopover({
}} }}
> >
<Box p={2}> <Box p={2}>
<Typography variant="h6" gutterBottom style={{backgroundColor:color}}> <Typography
variant="h6"
gutterBottom
style={{ backgroundColor: color }}
>
Create a Calendar Create a Calendar
</Typography> </Typography>
<TextField <TextField
+1 -1
View File
@@ -58,7 +58,7 @@ export function parseCalendarEvent(
case "organizer": case "organizer":
event.organizer = { event.organizer = {
cn: params?.cn ?? "", cn: params?.cn ?? "",
cal_address: value.replace(/^mailto:/, ""), cal_address: value?.replace(/^mailto:/, ""),
}; };
break; break;
case "attendee": case "attendee":
+5 -5
View File
@@ -1,13 +1,13 @@
body { body {
margin: 0; margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', font-family:
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu",
sans-serif; "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;
-webkit-font-smoothing: antialiased; -webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
} }
code { code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', font-family:
monospace; source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace;
} }
-15
View File
@@ -1,15 +0,0 @@
import { ReportHandler } from 'web-vitals';
const reportWebVitals = (onPerfEntry?: ReportHandler) => {
if (onPerfEntry && onPerfEntry instanceof Function) {
import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
getCLS(onPerfEntry);
getFID(onPerfEntry);
getFCP(onPerfEntry);
getLCP(onPerfEntry);
getTTFB(onPerfEntry);
});
}
};
export default reportWebVitals;