calendar list display improvments (#115)

* [#60] added collapsible groups

* [#60] factorized code and fixed tests

* fixup! [#60] added collapsible groups

* fixup! [#60] added collapsible groups

---------

Co-authored-by: Camille Moussu <cmoussu@linagora.com>
This commit is contained in:
Camille Moussu
2025-09-18 18:01:08 +02:00
committed by GitHub
parent b8cef20b9e
commit 8a7d2f742a
3 changed files with 136 additions and 61 deletions
+22 -1
View File
@@ -123,7 +123,7 @@ describe("CalendarSelection", () => {
renderWithProviders(<CalendarApp />, preloadedState);
expect(screen.getByText("Personnal Calendars")).toBeInTheDocument();
expect(screen.getByText("Delegated Calendars")).toBeInTheDocument();
expect(screen.getByText("Shared Calendars")).toBeInTheDocument();
expect(screen.getByText("Other Calendars")).toBeInTheDocument();
expect(screen.getByLabelText("Calendar personnal")).toBeInTheDocument();
expect(screen.getByLabelText("Calendar delegated")).toBeInTheDocument();
@@ -147,4 +147,25 @@ describe("CalendarSelection", () => {
expect(spy).toHaveBeenCalled();
});
});
it("open accordeon when clicking on button only", () => {
renderWithProviders(<CalendarApp />, preloadedState);
expect(screen.getByText("Personnal Calendars")).toBeInTheDocument();
expect(screen.getByText("Delegated Calendars")).toBeInTheDocument();
expect(screen.getByText("Other Calendars")).toBeInTheDocument();
expect(screen.getByLabelText("Calendar personnal")).toBeInTheDocument();
expect(screen.getByLabelText("Calendar delegated")).toBeInTheDocument();
expect(screen.getByLabelText("Calendar shared")).toBeInTheDocument();
const delegatedAccordionSummary = screen
.getByText("Delegated Calendars")
.closest(".MuiAccordionSummary-root");
const addButton = screen.getAllByTestId("AddIcon")[1];
fireEvent.click(addButton);
expect(delegatedAccordionSummary).toHaveAttribute("aria-expanded", "true");
fireEvent.click(addButton);
expect(delegatedAccordionSummary).toHaveAttribute("aria-expanded", "true");
});
});
@@ -50,7 +50,7 @@ describe("CalendarSelection", () => {
expect(screen.getByText("Personnal Calendars")).toBeInTheDocument();
expect(screen.getByText("Delegated Calendars")).toBeInTheDocument();
expect(screen.getByText("Shared Calendars")).toBeInTheDocument();
expect(screen.getByText("Other Calendars")).toBeInTheDocument();
expect(screen.getByLabelText("Calendar personnal")).toBeChecked();
expect(screen.getByLabelText("Calendar delegated")).not.toBeChecked();
@@ -113,7 +113,7 @@ describe("CalendarSelection", () => {
}
);
const addButton = screen.getByTestId("AddIcon");
const addButton = screen.getAllByTestId("AddIcon")[0];
fireEvent.click(addButton);
expect(screen.getByRole("presentation")).toBeInTheDocument();
+112 -58
View File
@@ -1,12 +1,78 @@
import { useAppDispatch, useAppSelector } from "../../app/hooks";
import Accordion from "@mui/material/Accordion";
import AccordionDetails from "@mui/material/AccordionDetails";
import AccordionSummary from "@mui/material/AccordionSummary";
import Typography from "@mui/material/Typography";
import { useAppSelector } from "../../app/hooks";
import AddIcon from "@mui/icons-material/Add";
import { useEffect, useState } from "react";
import { useState } from "react";
import CalendarPopover from "../../features/Calendars/CalendarModal";
import { Calendars } from "../../features/Calendars/CalendarTypes";
import MoreVertIcon from "@mui/icons-material/MoreVert";
import IconButton from "@mui/material/IconButton";
import Checkbox from "@mui/material/Checkbox";
import Button from "@mui/material/Button";
import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
function CalendarAccordion({
title,
calendars,
selectedCalendars,
handleToggle,
showAddButton = false,
onAddClick,
defaultExpanded = false,
setOpen,
}: {
title: string;
calendars: string[];
selectedCalendars: string[];
handleToggle: (id: string) => void;
showAddButton?: boolean;
onAddClick?: Function;
defaultExpanded?: boolean;
setOpen: Function;
}) {
const allCalendars = useAppSelector((state) => state.calendars.list);
if (calendars.length === 0) return null;
const [expended, setExpended] = useState(defaultExpanded);
return (
<Accordion defaultExpanded={defaultExpanded} expanded={expended}>
<AccordionSummary
expandIcon={<ExpandMoreIcon />}
aria-controls={`${title}-content`}
id={`${title}-header`}
className="calendarListHeader"
onClick={() => setExpended(!expended)}
>
<Typography component="h3">{title}</Typography>
{showAddButton && (
<IconButton
component="span"
onClick={(e) => {
expended && e.stopPropagation();
onAddClick && onAddClick();
}}
>
<AddIcon />
</IconButton>
)}
</AccordionSummary>
<AccordionDetails>
{calendars.map((id) =>
CalendarSelector(
allCalendars,
id,
selectedCalendars,
handleToggle,
() => setOpen(id)
)
)}
</AccordionDetails>
</Accordion>
);
}
export default function CalendarSelection({
selectedCalendars,
@@ -17,6 +83,7 @@ export default function CalendarSelection({
}) {
const userId = useAppSelector((state) => state.user.userData.openpaasId);
const calendars = useAppSelector((state) => state.calendars.list);
const personnalCalendars = Object.keys(calendars).filter(
(id) => id.split("/")[0] === userId
);
@@ -26,6 +93,7 @@ export default function CalendarSelection({
const sharedCalendars = Object.keys(calendars).filter(
(id) => id.split("/")[0] !== userId && !calendars[id].delegated
);
const handleCalendarToggle = (name: string) => {
setSelectedCalendars((prev: string[]) =>
prev.includes(name) ? prev.filter((n) => n !== name) : [...prev, name]
@@ -37,67 +105,53 @@ export default function CalendarSelection({
return (
<>
<div>
<div className="calendarListHeader">
<h3>Personnal Calendars</h3>
<Button onClick={() => setAnchorElCal(document.body)}>
<AddIcon />
</Button>
</div>
{personnalCalendars.map((id) =>
CalendarSelector(
calendars,
id,
selectedCalendars,
handleCalendarToggle,
() => {
setAnchorElCal(document.body);
setSelectedCalId(id);
}
)
)}
{delegatedCalendars.length > 0 && (
<>
<span className="calendarListHeader">
<h3>Delegated Calendars</h3>
</span>
{delegatedCalendars.map((id) =>
CalendarSelector(
calendars,
id,
selectedCalendars,
handleCalendarToggle,
() => {
setAnchorElCal(document.body);
setSelectedCalId(id);
}
)
)}
</>
)}
{sharedCalendars.length > 0 && (
<>
<span className="calendarListHeader">
<h3>Shared Calendars</h3>
</span>
{sharedCalendars.map((id) =>
CalendarSelector(
calendars,
id,
selectedCalendars,
handleCalendarToggle,
() => {
setAnchorElCal(document.body);
setSelectedCalId(id);
}
)
)}
</>
)}
<CalendarAccordion
title="Personnal Calendars"
calendars={personnalCalendars}
selectedCalendars={selectedCalendars}
handleToggle={handleCalendarToggle}
showAddButton
onAddClick={() => setAnchorElCal(document.body)}
setOpen={(id: string) => {
setAnchorElCal(document.body);
setSelectedCalId(id);
}}
defaultExpanded
/>
<CalendarAccordion
title="Delegated Calendars"
calendars={delegatedCalendars}
selectedCalendars={selectedCalendars}
handleToggle={handleCalendarToggle}
defaultExpanded
setOpen={(id: string) => {
setAnchorElCal(document.body);
setSelectedCalId(id);
}}
/>
<CalendarAccordion
title="Other Calendars"
calendars={sharedCalendars}
selectedCalendars={selectedCalendars}
showAddButton
onAddClick={() => {
setAnchorElCal(document.body);
}}
handleToggle={handleCalendarToggle}
setOpen={(id: string) => {
setAnchorElCal(document.body);
setSelectedCalId(id);
}}
/>
</div>
<CalendarPopover
anchorEl={anchorElCal}
open={Boolean(anchorElCal)}
onClose={() => {
setSelectedCalId("");
setAnchorElCal(null);
}}
calendar={calendars[selectedCalId] ?? undefined}