diff --git a/__test__/components/Calendar.test.tsx b/__test__/components/Calendar.test.tsx
index 29596e8..75e68ec 100644
--- a/__test__/components/Calendar.test.tsx
+++ b/__test__/components/Calendar.test.tsx
@@ -76,7 +76,7 @@ describe("CalendarSelection", () => {
delegated: true,
id: "user2/cal1",
color: { light: "#FF0000", dark: "#000" },
- owner: { emails: ["alice@example.com"] },
+ owner: { emails: ["alice@example.com"], lastname: "alice" },
events: {
event1: {
id: "event1",
@@ -107,7 +107,7 @@ describe("CalendarSelection", () => {
name: "Calendar shared",
id: "user3/cal1",
color: { light: "#FF0000", dark: "#000" },
- owner: { emails: ["alice@example.com"] },
+ owner: { emails: ["alice@example.com"], lastname: "alice" },
events: {
event1: {
id: "event1",
@@ -709,10 +709,10 @@ describe("calendar Availability search", () => {
);
const selectedCalls = spy.mock.calls.filter(
- (call: { calId: string }[]) => call[0].calId === "user1/cal1"
+ (call) => call[0].calId === "user1/cal1"
);
- const hiddenCalls = spy.mock.calls.filter(
- (call: { calId: string }[]) =>
+ spy.mock.calls.filter(
+ (call) =>
call[0].calId === "user1/cal2" || call[0].calId === "user1/cal3"
);
@@ -745,13 +745,12 @@ describe("calendar Availability search", () => {
);
const callsForCal1 = spy.mock.calls.filter(
- (call: { calId: string }[]) => call[0].calId === "user1/cal1"
+ (call) => call[0].calId === "user1/cal1"
);
const uniqueRanges = new Set(
callsForCal1.map(
- (call: { match: { end: string; start: string } }[]) =>
- `${call[0].match.start}_${call[0].match.end}`
+ (call) => `${call[0].match.start}_${call[0].match.end}`
)
);
@@ -762,8 +761,8 @@ describe("calendar Availability search", () => {
const stateWithUndefinedCalendars = {
...preloadedState,
calendars: {
- list: undefined as any,
- templist: undefined as any,
+ list: undefined,
+ templist: undefined,
pending: false,
},
};
@@ -783,7 +782,7 @@ describe("calendar Availability search", () => {
...preloadedState,
calendars: {
...preloadedState.calendars,
- templist: undefined as any,
+ templist: undefined,
},
};
diff --git a/__test__/components/CalendarColors.test.tsx b/__test__/components/CalendarColors.test.tsx
index 73843b2..45b6c25 100644
--- a/__test__/components/CalendarColors.test.tsx
+++ b/__test__/components/CalendarColors.test.tsx
@@ -73,7 +73,7 @@ describe("CalendarSearch", () => {
await act(async () => {
renderWithProviders(
- ,
+ ,
preloadedState
);
});
@@ -96,7 +96,7 @@ describe("CalendarSearch", () => {
});
await waitFor(() => {
- expect(screen.getByText("Test Calendar")).toBeInTheDocument();
+ expect(screen.getByText(/Test Calendar/i)).toBeInTheDocument();
expect(screen.getByText("user@example.com")).toBeInTheDocument();
});
});
@@ -117,7 +117,7 @@ describe("CalendarSearch", () => {
await act(async () => {
renderWithProviders(
- ,
+ ,
preloadedState
);
});
@@ -133,7 +133,7 @@ describe("CalendarSearch", () => {
});
await waitFor(() => {
- expect(screen.getByText("Test Calendar")).toBeInTheDocument();
+ expect(screen.getByText(/Test Calendar/i)).toBeInTheDocument();
});
const addButton = screen.getByRole("button", { name: /add/i });
@@ -170,7 +170,7 @@ describe("CalendarSearch", () => {
await act(async () => {
renderWithProviders(
- ,
+ ,
preloadedState
);
});
@@ -205,7 +205,7 @@ describe("CalendarSearch", () => {
await act(async () => {
renderWithProviders(
- ,
+ ,
preloadedState
);
});
@@ -237,7 +237,7 @@ describe("CalendarSearch", () => {
await act(async () => {
renderWithProviders(
- ,
+ ,
preloadedState
);
});
@@ -253,7 +253,7 @@ describe("CalendarSearch", () => {
});
await waitFor(() => {
- expect(screen.getByText("Test Calendar")).toBeInTheDocument();
+ expect(screen.getByText(/Test Calendar/i)).toBeInTheDocument();
});
// ColorPicker would need to be interacted with based on its implementation
@@ -286,7 +286,7 @@ describe("CalendarSearch", () => {
await act(async () => {
renderWithProviders(
- ,
+ ,
preloadedState
);
});
@@ -302,8 +302,8 @@ describe("CalendarSearch", () => {
});
await waitFor(() => {
- expect(screen.getByText("Test Calendar")).toBeInTheDocument();
- expect(screen.getByText("Second Calendar")).toBeInTheDocument();
+ expect(screen.getByText(/Test Calendar/i)).toBeInTheDocument();
+ expect(screen.getByText(/Second Calendar/i)).toBeInTheDocument();
});
});
@@ -316,7 +316,7 @@ describe("CalendarSearch", () => {
await act(async () => {
renderWithProviders(
- ,
+ ,
preloadedState
);
});
@@ -351,7 +351,7 @@ describe("CalendarSearch", () => {
await act(async () => {
renderWithProviders(
- ,
+ ,
preloadedState
);
});
@@ -378,7 +378,7 @@ describe("CalendarSearch", () => {
});
await waitFor(() => {
- expect(screen.getByText("Test Calendar")).toBeInTheDocument();
+ expect(screen.getByText(/Test Calendar/i)).toBeInTheDocument();
expect(screen.getByText("user@example.com")).toBeInTheDocument();
});
diff --git a/__test__/components/CalendarSelection.test.tsx b/__test__/components/CalendarSelection.test.tsx
index 2e94896..2d216c1 100644
--- a/__test__/components/CalendarSelection.test.tsx
+++ b/__test__/components/CalendarSelection.test.tsx
@@ -24,20 +24,28 @@ describe("CalendarSelection", () => {
name: "Calendar personal",
id: "user1/cal1",
color: "#FF0000",
- owner: { emails: ["alice@example.com"] },
+ owner: { emails: ["alice@example.com"], lastname: "alice" },
},
"user2/cal1": {
name: "Calendar delegated",
delegated: true,
id: "user2/cal1",
color: "#00FF00",
- owner: { emails: ["bob@example.com"] },
+ owner: {
+ firstname: "Bob",
+ lastname: "Builder",
+ emails: ["bob@example.com"],
+ },
},
"user3/cal1": {
name: "Calendar shared",
id: "user3/cal1",
color: "#0000FF",
- owner: { emails: ["charlie@example.com"] },
+ owner: {
+ firstname: "Charlie",
+ lastname: "Chaplin",
+ emails: ["charlie@example.com"],
+ },
},
};
beforeAll(() => {
@@ -273,4 +281,18 @@ describe("CalendarSelection", () => {
fireEvent.click(delegatedAccordionSummary!);
expect(delegatedAccordionSummary).toHaveAttribute("aria-expanded", "false");
});
+
+ it("renders owner name caption for non-personal, non-default calendars", () => {
+ renderWithProviders(
+ ,
+ { user: baseUser, calendars: { list: calendarsMock, pending: false } }
+ );
+ expect(screen.getByText("Bob Builder")).toBeInTheDocument();
+ expect(screen.getByText("Charlie Chaplin")).toBeInTheDocument();
+ // personal calendar should NOT show a caption
+ expect(screen.queryByText("alice")).not.toBeInTheDocument();
+ });
});
diff --git a/__test__/features/Calendars/CalendarAPI.test.tsx b/__test__/features/Calendars/CalendarAPI.test.tsx
index 8384793..21bdb22 100644
--- a/__test__/features/Calendars/CalendarAPI.test.tsx
+++ b/__test__/features/Calendars/CalendarAPI.test.tsx
@@ -114,7 +114,7 @@ describe("Calendar API", () => {
});
});
- it("get secret link ", async () => {
+ it("get secret link without reset", async () => {
const calLink = "/calendars/calId.json";
(api.get as jest.Mock).mockReturnValue({
json: jest.fn().mockResolvedValue("link"),
@@ -131,7 +131,7 @@ describe("Calendar API", () => {
}
);
});
- it("get secret link ", async () => {
+ it("get secret link with reset", async () => {
const calLink = "/calendars/calId.json";
(api.get as jest.Mock).mockReturnValue({
json: jest.fn().mockResolvedValue("link"),
@@ -162,7 +162,7 @@ describe("Calendar API", () => {
});
});
- it("When adding a sharedCal with #default as a name a new name is sent to the back", async () => {
+ it("When adding a sharedCal with #default #default is preserved", async () => {
const mockApiPost = jest.spyOn(api, "post");
const calData = {
@@ -192,12 +192,11 @@ describe("Calendar API", () => {
expect(mockApiPost).toHaveBeenCalledWith(
"dav/calendars/currentUserId.json",
expect.objectContaining({
- body: expect.stringContaining('"dav:name":"John Doe\'s calendar"'),
+ body: expect.stringContaining('"dav:name":"#default"'),
})
);
const callBody = JSON.parse(String(mockApiPost.mock.calls[0][1]?.body));
- expect(callBody["dav:name"]).toBe("John Doe's calendar");
- expect(callBody["dav:name"]).not.toBe("#default");
+ expect(callBody["dav:name"]).toBe("#default");
});
});
diff --git a/src/components/Calendar/CalendarColorPicker.tsx b/src/components/Calendar/CalendarColorPicker.tsx
index c856fd2..eba4f83 100644
--- a/src/components/Calendar/CalendarColorPicker.tsx
+++ b/src/components/Calendar/CalendarColorPicker.tsx
@@ -11,7 +11,8 @@ import {
import { useState, useEffect } from "react";
import { HexColorPicker } from "react-colorful";
import { useI18n } from "twake-i18n";
-import { defaultColors, getAccessiblePair } from "./utils/calendarColorsUtils";
+import { getAccessiblePair } from "./utils/calendarColorsUtils";
+import { defaultColors } from "@/utils/defaultColors";
export function ColorPicker({
selectedColor,
diff --git a/src/components/Calendar/CalendarModal.tsx b/src/components/Calendar/CalendarModal.tsx
index 195cd8b..5686f38 100644
--- a/src/components/Calendar/CalendarModal.tsx
+++ b/src/components/Calendar/CalendarModal.tsx
@@ -14,7 +14,7 @@ import { ResponsiveDialog } from "../Dialog";
import { AccessTab } from "./AccessTab";
import { ImportTab } from "./ImportTab";
import { SettingsTab } from "./SettingsTab";
-import { defaultColors } from "./utils/calendarColorsUtils";
+import { defaultColors } from "@/utils/defaultColors";
function CalendarPopover({
open,
diff --git a/src/components/Calendar/CalendarName.tsx b/src/components/Calendar/CalendarName.tsx
index 70006e9..8c81105 100644
--- a/src/components/Calendar/CalendarName.tsx
+++ b/src/components/Calendar/CalendarName.tsx
@@ -1,17 +1,42 @@
import { Calendar } from "@/features/Calendars/CalendarTypes";
import { Box, Typography } from "@linagora/twake-mui";
import SquareRoundedIcon from "@mui/icons-material/SquareRounded";
+import { defaultColors } from "@/utils/defaultColors";
+import { makeDisplayName } from "@/utils/makeDisplayName";
+import { OwnerCaption } from "./OwnerCaption";
+import { useAppSelector } from "@/app/hooks";
+
export function CalendarName({ calendar }: { calendar: Calendar }) {
+ const userData = useAppSelector((state) => state.user.userData);
+ const showCaption =
+ calendar.name !== "#default" &&
+ userData.openpaasId !== calendar.id.split("/")[0];
+
return (
-
+
- {calendar.name}
+
+
+ {calendar.name}
+
+
+
);
}
diff --git a/src/components/Calendar/CalendarSearch.tsx b/src/components/Calendar/CalendarSearch.tsx
index d543e5e..11045ad 100644
--- a/src/components/Calendar/CalendarSearch.tsx
+++ b/src/components/Calendar/CalendarSearch.tsx
@@ -20,7 +20,8 @@ import { PeopleSearch, User } from "../Attendees/PeopleSearch";
import { ResponsiveDialog } from "../Dialog";
import { stringAvatar } from "../Event/utils/eventUtils";
import { ColorPicker } from "./CalendarColorPicker";
-import { defaultColors, getAccessiblePair } from "./utils/calendarColorsUtils";
+import { getAccessiblePair } from "./utils/calendarColorsUtils";
+import { defaultColors } from "@/utils/defaultColors";
interface CalendarWithOwner {
cal: CalendarData;
diff --git a/src/components/Calendar/CalendarSelection.tsx b/src/components/Calendar/CalendarSelection.tsx
index 1eb6243..9174897 100644
--- a/src/components/Calendar/CalendarSelection.tsx
+++ b/src/components/Calendar/CalendarSelection.tsx
@@ -2,6 +2,7 @@ import { useAppDispatch, useAppSelector } from "@/app/hooks";
import { Calendar } from "@/features/Calendars/CalendarTypes";
import { removeCalendarAsync } from "@/features/Calendars/services";
import { extractEventBaseUuid } from "@/utils/extractEventBaseUuid";
+import { makeDisplayName } from "@/utils/makeDisplayName";
import { renameDefault } from "@/utils/renameDefault";
import { trimLongTextWithoutSpace } from "@/utils/textUtils";
import {
@@ -23,6 +24,7 @@ import { useI18n } from "twake-i18n";
import CalendarPopover from "./CalendarModal";
import CalendarSearch from "./CalendarSearch";
import { DeleteCalendarDialog } from "./DeleteCalendarDialog";
+import { OwnerCaption } from "./OwnerCaption";
function CalendarAccordion({
title,
@@ -260,6 +262,20 @@ function CalendarSelector({
() => trimLongTextWithoutSpace(calendars[id].name),
[calendars, id]
);
+
+ const ownerDisplayName = useMemo(
+ () => makeDisplayName(calendars[id]),
+ [calendars, id]
+ );
+
+ const displayName = useMemo(
+ () => renameDefault(trimmedName, ownerDisplayName ?? "", t, isPersonal),
+ [trimmedName, ownerDisplayName, t, isPersonal]
+ );
+
+ const showCaption =
+ !isPersonal && trimmedName !== "#default" && ownerDisplayName != null;
+
return (
<>
handleCalendarToggle(id)}
+ inputProps={{ "aria-label": displayName }}
/>
-
- {renameDefault(trimmedName, calendars[id].owner, t, isPersonal)}
-
+
+ {displayName}
+
+
+
diff --git a/src/components/Calendar/OwnerCaption.tsx b/src/components/Calendar/OwnerCaption.tsx
new file mode 100644
index 0000000..fb2e67b
--- /dev/null
+++ b/src/components/Calendar/OwnerCaption.tsx
@@ -0,0 +1,23 @@
+import { Typography } from "@linagora/twake-mui";
+
+export function OwnerCaption({
+ showCaption,
+ ownerDisplayName,
+}: {
+ showCaption: boolean;
+ ownerDisplayName: string;
+}) {
+ return (
+
+ {showCaption && ownerDisplayName}
+
+ );
+}
diff --git a/src/components/Calendar/utils/calendarColorsUtils.tsx b/src/components/Calendar/utils/calendarColorsUtils.tsx
index 978701c..9ca27cd 100644
--- a/src/components/Calendar/utils/calendarColorsUtils.tsx
+++ b/src/components/Calendar/utils/calendarColorsUtils.tsx
@@ -2,6 +2,7 @@ import { AppDispatch } from "@/app/store";
import { updateCalColor } from "@/features/Calendars/CalendarSlice";
import { Calendar } from "@/features/Calendars/CalendarTypes";
import { darken, getContrastRatio, lighten, Theme } from "@linagora/twake-mui";
+import { defaultColors } from "@/utils/defaultColors";
export function updateDarkColor(
calendars: Record,
@@ -53,10 +54,3 @@ export function getAccessiblePair(baseColor: string, theme: Theme): string {
return theme.palette.getContrastText(baseColor);
}
-
-export const defaultColors = [
- { light: "#D0ECDA", dark: "#329655" },
- { light: "#FAE3CE", dark: "#E15300" },
- { light: "#F5CFD0", dark: "#BE0103" },
- { light: "#AFCBEF", dark: "#0654B1" },
-];
diff --git a/src/components/Event/EventFormFields.tsx b/src/components/Event/EventFormFields.tsx
index e60b010..f4f59d5 100644
--- a/src/components/Event/EventFormFields.tsx
+++ b/src/components/Event/EventFormFields.tsx
@@ -2,6 +2,8 @@ import { Calendar } from "@/features/Calendars/CalendarTypes";
import { RepetitionObject } from "@/features/Events/EventsTypes";
import { userAttendee } from "@/features/User/models/attendee";
import iconCamera from "@/static/images/icon-camera.svg";
+import { defaultColors } from "@/utils/defaultColors";
+import { makeDisplayName } from "@/utils/makeDisplayName";
import {
addVideoConferenceToDescription,
generateMeetingLink,
@@ -35,6 +37,7 @@ import React from "react";
import { useI18n } from "twake-i18n";
import AttendeeSelector from "../Attendees/AttendeeSearch";
import { CalendarItemList } from "../Calendar/CalendarItemList";
+import { OwnerCaption } from "../Calendar/OwnerCaption";
import { SnackbarAlert } from "../Loading/SnackBarAlert";
import { TimezoneAutocomplete } from "../Timezone/TimezoneAutocomplete";
import { AddDescButton } from "./AddDescButton";
@@ -775,7 +778,7 @@ export default function EventFormFields({
sx={{
color:
userPersonalCalendars.find((cal) => cal.id === calendarid)
- ?.color?.light ?? "#3788D8",
+ ?.color?.light ?? defaultColors[0].light,
width: 24,
height: 24,
}}
@@ -783,8 +786,34 @@ export default function EventFormFields({
}
onClick={() => setHasClickedCalendarSection(true)}
>
- {userPersonalCalendars.find((cal) => cal.id === calendarid)?.name ||
- t("event.form.calendar")}
+ {userPersonalCalendars.find((cal) => cal.id === calendarid)
+ ?.name ? (
+
+
+ {
+ userPersonalCalendars.find((cal) => cal.id === calendarid)
+ ?.name
+ }
+
+ cal.id === calendarid) !==
+ undefined &&
+ userPersonalCalendars.find((cal) => cal.id === calendarid)
+ ?.name !== "#default"
+ }
+ ownerDisplayName={
+ makeDisplayName(
+ userPersonalCalendars.find(
+ (cal) => cal.id === calendarid
+ ) ?? ({} as Calendar)
+ ) ?? ""
+ }
+ />
+
+ ) : (
+ t("event.form.calendar")
+ )}
) : (
diff --git a/src/features/Calendars/CalendarApi.ts b/src/features/Calendars/CalendarApi.ts
index e8dec88..ab69619 100644
--- a/src/features/Calendars/CalendarApi.ts
+++ b/src/features/Calendars/CalendarApi.ts
@@ -69,15 +69,10 @@ export async function addSharedCalendar(
body: JSON.stringify({
id: calId,
...cal.cal,
- "dav:name":
- cal.cal["dav:name"] === "#default"
- ? (cal.owner?.displayName ?? "Unknown") + "'s calendar"
- : cal.cal["dav:name"],
"calendarserver:source": {
acl: cal.cal.acl,
calendarHomeId: cal.cal.id,
color: cal.cal["apple:color"],
-
description: cal.cal["caldav:description"],
href: cal.cal._links.self.href,
id: cal.cal.id,
diff --git a/src/features/Calendars/services/getCalendarDetailAsync.ts b/src/features/Calendars/services/getCalendarDetailAsync.ts
index 84c067f..ed685ac 100644
--- a/src/features/Calendars/services/getCalendarDetailAsync.ts
+++ b/src/features/Calendars/services/getCalendarDetailAsync.ts
@@ -8,7 +8,7 @@ import { createAsyncThunk } from "@reduxjs/toolkit";
import { getCalendar } from "../CalendarApi";
import { RejectedError } from "../types/RejectedError";
import { extractCalendarEvents } from "../utils/extractCalendarEvents";
-import { defaultColors } from "@/components/Calendar/utils/calendarColorsUtils";
+import { defaultColors } from "@/utils/defaultColors";
import { type RootState } from "@/app/store";
export const getCalendarDetailAsync = createAsyncThunk<
diff --git a/src/features/Calendars/services/getCalendarsListAsync.ts b/src/features/Calendars/services/getCalendarsListAsync.ts
index ab08bfb..71b908a 100644
--- a/src/features/Calendars/services/getCalendarsListAsync.ts
+++ b/src/features/Calendars/services/getCalendarsListAsync.ts
@@ -1,4 +1,5 @@
import { RootState } from "@/app/store";
+import { defaultColors } from "@/utils/defaultColors";
import { OpenPaasUserData } from "@/features/User/type/OpenPaasUserData";
import { getOpenPaasUser, getUserDetails } from "@/features/User/userAPI";
import { formatReduxError } from "@/utils/errorUtils";
@@ -75,10 +76,12 @@ export const getCalendarsListAsync = createAsyncThunk<
emails: [],
};
- const color = {
- light: cal["apple:color"] ?? "#006BD8",
- dark: "#FFF",
- };
+ const color = cal["apple:color"]
+ ? {
+ light: cal["apple:color"],
+ dark: "#FFF",
+ }
+ : defaultColors[0];
fetchedCalendars[id] = {
id,
name: cal["dav:name"] ?? "",
diff --git a/src/features/Calendars/utils/extractCalendarEvents.ts b/src/features/Calendars/utils/extractCalendarEvents.ts
index 5fedf56..3eb7df6 100644
--- a/src/features/Calendars/utils/extractCalendarEvents.ts
+++ b/src/features/Calendars/utils/extractCalendarEvents.ts
@@ -1,4 +1,4 @@
-import { defaultColors } from "@/components/Calendar/utils/calendarColorsUtils";
+import { defaultColors } from "@/utils/defaultColors";
import { CalendarEvent } from "@/features/Events/EventsTypes";
import { parseCalendarEvent } from "@/features/Events/eventUtils";
import { CalDavItem } from "../api/types";
diff --git a/src/features/Search/SearchResultsPage.tsx b/src/features/Search/SearchResultsPage.tsx
index 318911a..022baca 100644
--- a/src/features/Search/SearchResultsPage.tsx
+++ b/src/features/Search/SearchResultsPage.tsx
@@ -1,5 +1,6 @@
import { useAppDispatch, useAppSelector } from "@/app/hooks";
import { AppDispatch } from "@/app/store";
+import { defaultColors } from "@/utils/defaultColors";
import { browserDefaultTimeZone } from "@/utils/timezone";
import {
Box,
@@ -239,7 +240,7 @@ function ResultItem({
string,
isOwnCalendar?: boolean
) {
+ if (!ownerName) {
+ if (davName && davName !== "#default") return davName;
+ }
if (!davName) {
return t("calendar.defaultCalendarName", { name: ownerName });
}