From 20216ba6c331f379951deb9103cb433d222fa10c Mon Sep 17 00:00:00 2001 From: aleckvincent Date: Wed, 13 Nov 2024 10:18:47 +0100 Subject: [PATCH] add open observation on click --- .../__tests__/mission-list-item.test.tsx | 47 ++++++++++++++----- .../elements/mission-list-header.tsx | 6 +-- .../components/elements/mission-list-item.tsx | 41 ++++++++++++---- .../components/elements/mission-listing.tsx | 13 +++-- .../components/element/mission-list-ulam.tsx | 4 +- .../components/ui/mission-list-ulam-title.tsx | 2 +- 6 files changed, 81 insertions(+), 32 deletions(-) diff --git a/frontend/src/v2/features/common/components/elements/__tests__/mission-list-item.test.tsx b/frontend/src/v2/features/common/components/elements/__tests__/mission-list-item.test.tsx index c40a707c..81784346 100644 --- a/frontend/src/v2/features/common/components/elements/__tests__/mission-list-item.test.tsx +++ b/frontend/src/v2/features/common/components/elements/__tests__/mission-list-item.test.tsx @@ -2,6 +2,8 @@ import { render, screen, fireEvent } from '../../../../../../test-utils.tsx' import MissionListItem from '../mission-list-item.tsx' import { ControlUnit } from '@mtes-mct/monitor-ui' import ControlUnitResourceType = ControlUnit.ControlUnitResourceType +import { Mission } from '@common/types/mission-types.ts' +import MissionListing from '../mission-listing.tsx' const mission1 = { id: 1, @@ -31,20 +33,27 @@ const queryAllMissionItemProps = () => ({ }) describe('MissionListItem component', () => { + const missions: Mission[] = [ + { id: 1, missionSource: 'source1', startDateTimeUtc: new Date().toISOString(), observationsByUnit: 'Observation 1' }, + { id: 2, missionSource: 'source2', startDateTimeUtc: new Date().toISOString(), observationsByUnit: 'Observation 2' }, + ]; + + const mockSetOpenIndex = vi.fn() + test('should render "N/A" if no ControlUnitResource provided', () => { - render() + render() const noResource = screen.queryByText("N/A") expect(noResource).toBeInTheDocument() }) test('should render "Voiture" if ControlUnitResource contains CAR', () => { - render() + render() const car = screen.queryByTestId("mission-list-item-control_unit_resources") expect(car).toHaveTextContent('Voiture') }) test('should render all properties for ulam', () => { - render() + render() const { missionIcon, missionNumber, openBy, openDate, ressourcesUsed, crew, missionStatus, reportStatus @@ -61,7 +70,7 @@ describe('MissionListItem component', () => { }) test('should not render ulam props when isUlam set to false', () => { - render() + render() const { missionIcon, missionNumber, openBy, openDate, ressourcesUsed, crew, missionStatus, reportStatus @@ -78,27 +87,39 @@ describe('MissionListItem component', () => { }) test('should render crew list with an ellipsis', () => { - render( ) + render( ) const missionCrew = screen.queryByTestId("mission-list-item-crew__text") expect(missionCrew).toHaveStyle('text-overflow: ellipsis') expect(missionCrew).toHaveStyle(`overflow: hidden`) expect(missionCrew).toHaveStyle(`white-space: nowrap`) }) - test('should displays details on mouse over and hides on mouse leave', () => { - render(); + it('should open an item on click and close others', () => { + render(); + + const missionItems = screen.getAllByTestId('mission-list-item-with-hover'); - const listItem = screen.getByTestId('mission-list-item-with-hover'); + fireEvent.click(missionItems[0]); + expect(screen.getByText('Observation 1')).toBeVisible(); - expect(screen.queryByTestId('mission-list-item-more')).toBeNull(); + expect(screen.queryByText('Observation 2')).toBeNull(); - fireEvent.mouseOver(listItem); + fireEvent.click(missionItems[1]); + expect(screen.getByText('Observation 2')).toBeVisible(); + expect(screen.queryByText('Observation 1')).toBeNull(); + }); + + it('should close the item when clicking outside', () => { + render(); - expect(screen.getByTestId('mission-list-item-more')).toBeInTheDocument(); + const missionItems = screen.getAllByTestId('mission-list-item-with-hover'); - fireEvent.mouseLeave(listItem); + fireEvent.click(missionItems[0]); + expect(screen.getByText('Observation 1')).toBeVisible(); - expect(screen.queryByTestId('mission-list-item-more')).toBeNull(); + fireEvent.mouseDown(document); + expect(screen.queryByText('Observation 1')).toBeNull(); }); + }) diff --git a/frontend/src/v2/features/common/components/elements/mission-list-header.tsx b/frontend/src/v2/features/common/components/elements/mission-list-header.tsx index 13bc9b2b..cdfc297c 100644 --- a/frontend/src/v2/features/common/components/elements/mission-list-header.tsx +++ b/frontend/src/v2/features/common/components/elements/mission-list-header.tsx @@ -12,17 +12,17 @@ const MissionListHeader: React.FC = ({ isUlam}) => { - +

Date d'ouverture

{isUlam && ( <> - +

Moyen(s) utilisé(s)

- +

Agent(s)

diff --git a/frontend/src/v2/features/common/components/elements/mission-list-item.tsx b/frontend/src/v2/features/common/components/elements/mission-list-item.tsx index 2586aee1..8d28ab2e 100644 --- a/frontend/src/v2/features/common/components/elements/mission-list-item.tsx +++ b/frontend/src/v2/features/common/components/elements/mission-list-item.tsx @@ -1,5 +1,5 @@ import { Mission } from '@common/types/mission-types.ts'; -import React, { useState } from 'react'; +import React, { useEffect, useRef, useState } from 'react' import { Icon, THEME } from '@mtes-mct/monitor-ui'; import { Divider, FlexboxGrid } from 'rsuite'; import { Link } from 'react-router-dom'; @@ -15,6 +15,9 @@ import { ULAM_V2_HOME_PATH } from '@router/router.tsx' interface MissionListItemProps { mission?: Mission; isUlam: boolean; + index: number; + openIndex: number | null; + setOpenIndex: (index: number | null) => void; } const ListItemWithHover = styled.div` @@ -36,17 +39,37 @@ const MissionCrewItem = styled.div` padding-right: 8px; ` -const MissionListItem: React.FC = ({ mission, isUlam }) => { +const MissionListItem: React.FC = ({ mission, isUlam, index, openIndex, setOpenIndex }) => { const controlUnitResourcesText = useControlUnitResourceLabel(mission?.controlUnits); const missionName = formatDateForMissionName(mission?.startDateTimeUtc); const missionDate = formatDateForFrenchHumans(mission?.startDateTimeUtc); const missionCrew = useCrewForMissionList(mission?.crew); - const [displayDetails, setDisplayDetails] = useState(false); + + const listItemRef = useRef(null); + const isOpen = openIndex === index; + + useEffect(() => { + const handleClickOutside = (event: MouseEvent) => { + if (listItemRef.current && !listItemRef.current.contains(event.target as Node)) { + setOpenIndex(null); + } + }; + + if (isOpen) { + document.addEventListener("mousedown", handleClickOutside); + } else { + document.removeEventListener("mousedown", handleClickOutside); + } + + return () => { + document.removeEventListener("mousedown", handleClickOutside); + }; + }, [isOpen, setOpenIndex]); return ( setDisplayDetails(true)} - onMouseLeave={() => setDisplayDetails(false)} + ref={listItemRef} + onClick={() => setOpenIndex(isOpen ? null : index)} data-testid="mission-list-item-with-hover" > = ({ mission, isUlam }) =>
- +

Mission n°{missionName}

@@ -78,12 +101,12 @@ const MissionListItem: React.FC = ({ mission, isUlam }) => {isUlam && ( <> - +

{controlUnitResourcesText}

- + {missionCrew.text} @@ -114,7 +137,7 @@ const MissionListItem: React.FC = ({ mission, isUlam }) => - {(displayDetails && mission?.observationsByUnit) && ( + {(isOpen && mission?.observationsByUnit) && ( = ({missions, isUlam}) => { + const [openIndex, setOpenIndex] = useState(null); return ( - {missions?.map((mission) => ( - + {missions?.map((mission, index) => ( + )) } diff --git a/frontend/src/v2/features/ulam/components/element/mission-list-ulam.tsx b/frontend/src/v2/features/ulam/components/element/mission-list-ulam.tsx index 1d78b20f..ca01a528 100644 --- a/frontend/src/v2/features/ulam/components/element/mission-list-ulam.tsx +++ b/frontend/src/v2/features/ulam/components/element/mission-list-ulam.tsx @@ -1,8 +1,6 @@ import React, { JSX } from 'react' import { Link, useNavigate } from 'react-router-dom' import { Col, Container, FlexboxGrid, Loader, Stack } from 'rsuite' -import MissionListing from '../../../common/components/elements/mission-listing.tsx' -import MissionListDaterangeNavigator from '../../../common/components/elements/mission-list-daterange-navigator.tsx' import { Icon } from '@mtes-mct/monitor-ui' interface MissionListUlamProps { dateRangeNavigator: JSX.Element, @@ -16,7 +14,7 @@ const MissionListUlam: React.FC = ({dateRangeNavigator, mi justify="center" style={{ padding: '4rem 2rem', display: 'flex', flex: 1 }} > - +

Missions

Mes rapports de mission

diff --git a/frontend/src/v2/features/ulam/components/ui/mission-list-ulam-title.tsx b/frontend/src/v2/features/ulam/components/ui/mission-list-ulam-title.tsx index 75bbf950..f163f39b 100644 --- a/frontend/src/v2/features/ulam/components/ui/mission-list-ulam-title.tsx +++ b/frontend/src/v2/features/ulam/components/ui/mission-list-ulam-title.tsx @@ -1,7 +1,7 @@ import React from 'react' const MissionListUlamTitle: React.FC = () => { - return

Rapport Nav | Ulam 33

+ return

Rapport Nav

} export default MissionListUlamTitle