Merge pull request #102 from linagora/72-event-action-duplicate-event
72 event action duplicate event
This commit is contained in:
@@ -0,0 +1,176 @@
|
|||||||
|
import { fireEvent, screen } from "@testing-library/react";
|
||||||
|
import EventDuplication from "../../src/components/Event/EventDuplicate";
|
||||||
|
import EventDisplayModal from "../../src/features/Events/EventDisplay";
|
||||||
|
import EventPopover from "../../src/features/Events/EventModal";
|
||||||
|
import { renderWithProviders } from "../utils/Renderwithproviders";
|
||||||
|
import EventPreviewModal from "../../src/features/Events/EventDisplayPreview";
|
||||||
|
|
||||||
|
const day = new Date();
|
||||||
|
const preloadedState = {
|
||||||
|
user: {
|
||||||
|
userData: {
|
||||||
|
sub: "test",
|
||||||
|
email: "test@test.com",
|
||||||
|
sid: "aiYbWZSk2g0F+LrQeD7Dg4QcUMR8R/zTZdZBiA7N6Ro",
|
||||||
|
openpaasId: "667037022b752d0026472254",
|
||||||
|
},
|
||||||
|
organiserData: {
|
||||||
|
cn: "test",
|
||||||
|
cal_address: "mailto:test@test.com",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
calendars: {
|
||||||
|
list: {
|
||||||
|
"667037022b752d0026472254/cal1": {
|
||||||
|
id: "667037022b752d0026472254/cal1",
|
||||||
|
name: "Calendar",
|
||||||
|
color: "#FF0000",
|
||||||
|
events: {
|
||||||
|
event1: {
|
||||||
|
uid: "event1",
|
||||||
|
URL: "calendars/667037022b752d0026472254/cal1/event1.ics",
|
||||||
|
title: "Test Event",
|
||||||
|
calId: "667037022b752d0026472254/cal1",
|
||||||
|
start: day,
|
||||||
|
end: day,
|
||||||
|
timezone: "UTC",
|
||||||
|
organizer: { cn: "test", cal_address: "test@test.com" },
|
||||||
|
attendee: [
|
||||||
|
{
|
||||||
|
cn: "test",
|
||||||
|
cal_address: "test@test.com",
|
||||||
|
partstat: "NEEDS-ACTION",
|
||||||
|
rsvp: "TRUE",
|
||||||
|
role: "REQ-PARTICIPANT",
|
||||||
|
cutype: "INDIVIDUAL",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cn: "John",
|
||||||
|
cal_address: "john@test.com",
|
||||||
|
partstat: "NEEDS-ACTION",
|
||||||
|
rsvp: "TRUE",
|
||||||
|
role: "REQ-PARTICIPANT",
|
||||||
|
cutype: "INDIVIDUAL",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
pending: false,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
describe("EventDuplication", () => {
|
||||||
|
it("opens EventPopover when button clicked", () => {
|
||||||
|
const handleClose = jest.fn();
|
||||||
|
renderWithProviders(
|
||||||
|
<EventDuplication
|
||||||
|
event={
|
||||||
|
preloadedState.calendars.list["667037022b752d0026472254/cal1"].events
|
||||||
|
.event1
|
||||||
|
}
|
||||||
|
onClose={handleClose}
|
||||||
|
/>,
|
||||||
|
preloadedState
|
||||||
|
);
|
||||||
|
|
||||||
|
const button = screen.getByRole("button");
|
||||||
|
fireEvent.click(button);
|
||||||
|
|
||||||
|
expect(screen.getByText(/Duplicate Event/i)).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("calls onClose when closing the popover", () => {
|
||||||
|
const handleClose = jest.fn();
|
||||||
|
renderWithProviders(
|
||||||
|
<EventDuplication
|
||||||
|
event={
|
||||||
|
preloadedState.calendars.list["667037022b752d0026472254/cal1"].events
|
||||||
|
.event1
|
||||||
|
}
|
||||||
|
onClose={handleClose}
|
||||||
|
/>,
|
||||||
|
preloadedState
|
||||||
|
);
|
||||||
|
|
||||||
|
fireEvent.click(screen.getByRole("button"));
|
||||||
|
|
||||||
|
fireEvent.click(screen.getByRole("button", { name: /Cancel/i }));
|
||||||
|
|
||||||
|
expect(handleClose).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("EventPopover", () => {
|
||||||
|
it("renders with event data", () => {
|
||||||
|
renderWithProviders(
|
||||||
|
<EventPopover
|
||||||
|
anchorEl={null}
|
||||||
|
open={true}
|
||||||
|
onClose={jest.fn()}
|
||||||
|
selectedRange={null}
|
||||||
|
setSelectedRange={jest.fn()}
|
||||||
|
calendarRef={{ current: null }}
|
||||||
|
event={
|
||||||
|
preloadedState.calendars.list["667037022b752d0026472254/cal1"].events
|
||||||
|
.event1
|
||||||
|
}
|
||||||
|
/>,
|
||||||
|
preloadedState
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(screen.getByDisplayValue(/Test Event/i)).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("saves duplicated event when Save is clicked", () => {
|
||||||
|
const onClose = jest.fn();
|
||||||
|
|
||||||
|
renderWithProviders(
|
||||||
|
<EventPopover
|
||||||
|
anchorEl={null}
|
||||||
|
open={true}
|
||||||
|
onClose={onClose}
|
||||||
|
selectedRange={null}
|
||||||
|
setSelectedRange={jest.fn()}
|
||||||
|
calendarRef={{ current: null }}
|
||||||
|
event={
|
||||||
|
preloadedState.calendars.list["667037022b752d0026472254/cal1"].events
|
||||||
|
.event1
|
||||||
|
}
|
||||||
|
/>,
|
||||||
|
preloadedState
|
||||||
|
);
|
||||||
|
|
||||||
|
fireEvent.change(screen.getByLabelText(/Title/i), {
|
||||||
|
target: { value: "Duplicated Event" },
|
||||||
|
});
|
||||||
|
fireEvent.click(screen.getByRole("button", { name: /Save/i }));
|
||||||
|
|
||||||
|
expect(onClose).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("EventDisplayModal", () => {
|
||||||
|
it("shows duplication button and opens duplication form", () => {
|
||||||
|
renderWithProviders(
|
||||||
|
<EventPreviewModal
|
||||||
|
eventId="event1"
|
||||||
|
calId="667037022b752d0026472254/cal1"
|
||||||
|
open={true}
|
||||||
|
onClose={jest.fn()}
|
||||||
|
anchorPosition={{ top: 0, left: 0 }}
|
||||||
|
/>,
|
||||||
|
preloadedState
|
||||||
|
);
|
||||||
|
|
||||||
|
fireEvent.click(screen.getByTestId("AddToPhotosIcon"));
|
||||||
|
expect(screen.getByText(/Duplicate Event/i)).toBeInTheDocument();
|
||||||
|
expect(
|
||||||
|
screen.getByDisplayValue(
|
||||||
|
preloadedState.calendars.list["667037022b752d0026472254/cal1"].events
|
||||||
|
.event1.title
|
||||||
|
)
|
||||||
|
).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1137,13 +1137,7 @@ describe("Event Full Display", () => {
|
|||||||
|
|
||||||
const day = new Date();
|
const day = new Date();
|
||||||
const preloadedTwoCals = {
|
const preloadedTwoCals = {
|
||||||
user: {
|
...preloadedState,
|
||||||
userData: {
|
|
||||||
sub: "test",
|
|
||||||
email: "test@test.com",
|
|
||||||
openpaasId: "667037022b752d0026472254",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
calendars: {
|
calendars: {
|
||||||
list: {
|
list: {
|
||||||
"667037022b752d0026472254/cal1": {
|
"667037022b752d0026472254/cal1": {
|
||||||
@@ -1210,13 +1204,7 @@ describe("Event Full Display", () => {
|
|||||||
|
|
||||||
const day = new Date();
|
const day = new Date();
|
||||||
const preloadedRecurrence = {
|
const preloadedRecurrence = {
|
||||||
user: {
|
...preloadedState,
|
||||||
userData: {
|
|
||||||
sub: "test",
|
|
||||||
email: "test@test.com",
|
|
||||||
openpaasId: "667037022b752d0026472254",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
calendars: {
|
calendars: {
|
||||||
list: {
|
list: {
|
||||||
"667037022b752d0026472254/cal1": {
|
"667037022b752d0026472254/cal1": {
|
||||||
|
|||||||
@@ -0,0 +1,52 @@
|
|||||||
|
import { IconButton } from "@mui/material";
|
||||||
|
import { CalendarEvent } from "../../features/Events/EventsTypes";
|
||||||
|
import AddToPhotosIcon from "@mui/icons-material/AddToPhotos";
|
||||||
|
import { useRef, useState } from "react";
|
||||||
|
import EventPopover from "../../features/Events/EventModal";
|
||||||
|
import { CalendarApi, DateSelectArg } from "@fullcalendar/core";
|
||||||
|
|
||||||
|
export default function EventDuplication({
|
||||||
|
onClose,
|
||||||
|
event,
|
||||||
|
}: {
|
||||||
|
onClose: Function;
|
||||||
|
event: CalendarEvent;
|
||||||
|
}) {
|
||||||
|
const [openModal, setOpenModal] = useState(false);
|
||||||
|
const [selectedRange, setSelectedRange] = useState<DateSelectArg | null>({
|
||||||
|
start: new Date(event.start),
|
||||||
|
startStr: new Date(event.start).toISOString(),
|
||||||
|
end: new Date(event.end ?? ""),
|
||||||
|
endStr: new Date(event.end ?? "").toISOString(),
|
||||||
|
allDay: event.allday ?? false,
|
||||||
|
} as DateSelectArg);
|
||||||
|
const calendarRef = useRef<CalendarApi | null>(null);
|
||||||
|
|
||||||
|
const handleClosePopover = () => {
|
||||||
|
calendarRef.current?.unselect();
|
||||||
|
setSelectedRange(null);
|
||||||
|
setOpenModal(!openModal);
|
||||||
|
onClose({}, "backdropClick");
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<IconButton
|
||||||
|
size="small"
|
||||||
|
onClick={() => {
|
||||||
|
setOpenModal(true);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<AddToPhotosIcon fontSize="small" />
|
||||||
|
</IconButton>
|
||||||
|
<EventPopover
|
||||||
|
anchorEl={null}
|
||||||
|
open={openModal}
|
||||||
|
selectedRange={selectedRange}
|
||||||
|
setSelectedRange={setSelectedRange}
|
||||||
|
calendarRef={calendarRef}
|
||||||
|
onClose={handleClosePopover}
|
||||||
|
event={event}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -39,6 +39,7 @@ import EventDisplayModal, {
|
|||||||
} from "./EventDisplay";
|
} from "./EventDisplay";
|
||||||
import { getEvent } from "./EventApi";
|
import { getEvent } from "./EventApi";
|
||||||
import { CalendarEvent } from "./EventsTypes";
|
import { CalendarEvent } from "./EventsTypes";
|
||||||
|
import EventDuplication from "../../components/Event/EventDuplicate";
|
||||||
|
|
||||||
export default function EventPreviewModal({
|
export default function EventPreviewModal({
|
||||||
eventId,
|
eventId,
|
||||||
@@ -122,6 +123,7 @@ export default function EventPreviewModal({
|
|||||||
gap: 1,
|
gap: 1,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
<EventDuplication event={event} onClose={onClose} />
|
||||||
{mailSpaUrl && attendees.length > 0 && (
|
{mailSpaUrl && attendees.length > 0 && (
|
||||||
<IconButton
|
<IconButton
|
||||||
size="small"
|
size="small"
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ function EventPopover({
|
|||||||
selectedRange,
|
selectedRange,
|
||||||
setSelectedRange,
|
setSelectedRange,
|
||||||
calendarRef,
|
calendarRef,
|
||||||
|
event,
|
||||||
}: {
|
}: {
|
||||||
anchorEl: HTMLElement | null;
|
anchorEl: HTMLElement | null;
|
||||||
open: boolean;
|
open: boolean;
|
||||||
@@ -40,6 +41,7 @@ function EventPopover({
|
|||||||
selectedRange: DateSelectArg | null;
|
selectedRange: DateSelectArg | null;
|
||||||
setSelectedRange: Function;
|
setSelectedRange: Function;
|
||||||
calendarRef: React.RefObject<CalendarApi | null>;
|
calendarRef: React.RefObject<CalendarApi | null>;
|
||||||
|
event?: CalendarEvent;
|
||||||
}) {
|
}) {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
|
|
||||||
@@ -63,20 +65,32 @@ function EventPopover({
|
|||||||
const timezones = TIMEZONES.aliases;
|
const timezones = TIMEZONES.aliases;
|
||||||
const [showMore, setShowMore] = useState(false);
|
const [showMore, setShowMore] = useState(false);
|
||||||
|
|
||||||
const [title, setTitle] = useState("");
|
const [title, setTitle] = useState(event?.title ?? "");
|
||||||
const [description, setDescription] = useState("");
|
const [description, setDescription] = useState(event?.description ?? "");
|
||||||
const [location, setLocation] = useState("");
|
const [location, setLocation] = useState(event?.location ?? "");
|
||||||
const [start, setStart] = useState("");
|
const [start, setStart] = useState(
|
||||||
const [end, setEnd] = useState("");
|
event?.start ? new Date(event.start).toISOString() : ""
|
||||||
const [calendarid, setCalendarid] = useState(0);
|
|
||||||
const [allday, setAllDay] = useState(false);
|
|
||||||
const [repetition, setRepetition] = useState<RepetitionObject>(
|
|
||||||
{} as RepetitionObject
|
|
||||||
);
|
);
|
||||||
const [attendees, setAttendees] = useState<userAttendee[]>([]);
|
const [end, setEnd] = useState(
|
||||||
const [alarm, setAlarm] = useState("");
|
event?.end ? new Date(event.end)?.toISOString() : ""
|
||||||
const [eventClass, setEventClass] = useState("PUBLIC");
|
);
|
||||||
const [busy, setBusy] = useState("OPAQUE");
|
const [calendarid, setCalendarid] = useState(
|
||||||
|
event?.calId
|
||||||
|
? userPersonnalCalendars.findIndex((e) => e.id === event?.calId)
|
||||||
|
: 0
|
||||||
|
);
|
||||||
|
const [allday, setAllDay] = useState(event?.allday ?? false);
|
||||||
|
const [repetition, setRepetition] = useState<RepetitionObject>(
|
||||||
|
event?.repetition ?? ({} as RepetitionObject)
|
||||||
|
);
|
||||||
|
const [attendees, setAttendees] = useState<userAttendee[]>(
|
||||||
|
event?.attendee
|
||||||
|
? event.attendee.filter((a) => a.cal_address !== organizer.cal_address)
|
||||||
|
: []
|
||||||
|
);
|
||||||
|
const [alarm, setAlarm] = useState(event?.alarm?.trigger ?? "");
|
||||||
|
const [eventClass, setEventClass] = useState(event?.class ?? "PUBLIC");
|
||||||
|
const [busy, setBusy] = useState(event?.transp ?? "OPAQUE");
|
||||||
|
|
||||||
const [timezone, setTimezone] = useState(
|
const [timezone, setTimezone] = useState(
|
||||||
Intl.DateTimeFormat().resolvedOptions().timeZone
|
Intl.DateTimeFormat().resolvedOptions().timeZone
|
||||||
@@ -157,7 +171,7 @@ function EventPopover({
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Card>
|
<Card>
|
||||||
<CardHeader title="Create Event" />
|
<CardHeader title={event ? "Duplicate Event" : "Create Event"} />
|
||||||
<CardContent
|
<CardContent
|
||||||
sx={{ maxHeight: "85vh", maxWidth: "40vw", overflow: "auto" }}
|
sx={{ maxHeight: "85vh", maxWidth: "40vw", overflow: "auto" }}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ export interface CalendarEvent {
|
|||||||
status?: string;
|
status?: string;
|
||||||
timezone: string;
|
timezone: string;
|
||||||
repetition?: RepetitionObject;
|
repetition?: RepetitionObject;
|
||||||
alarm: AlarmObject;
|
alarm?: AlarmObject;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RepetitionObject {
|
export interface RepetitionObject {
|
||||||
|
|||||||
Reference in New Issue
Block a user