From 2055b7e2416c98b07a159501b8949d1e7c5df7c3 Mon Sep 17 00:00:00 2001
From: Fanna Lautenbach <lautenbach@astron.nl>
Date: Tue, 15 Aug 2023 14:24:04 +0200
Subject: [PATCH] finish DateTimeNavigation component with tests and add
 related constants + fix layouts

---
 .../src/layout/sass/_timeline.scss            |   3 +
 .../components/toolbar/DateTimeInfo.js        |   2 -
 .../components/toolbar/DateTimeNavigator.js   | 178 ++++++------------
 .../toolbar/DateTimeNavigator.test.js         |  83 ++++++++
 .../tmss_webapp/src/utils/ui.constants.js     |   1 +
 5 files changed, 140 insertions(+), 127 deletions(-)
 create mode 100644 SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/components/toolbar/DateTimeNavigator.test.js

diff --git a/SAS/TMSS/frontend/tmss_webapp/src/layout/sass/_timeline.scss b/SAS/TMSS/frontend/tmss_webapp/src/layout/sass/_timeline.scss
index 234ceaa63be..98f3f112fe9 100644
--- a/SAS/TMSS/frontend/tmss_webapp/src/layout/sass/_timeline.scss
+++ b/SAS/TMSS/frontend/tmss_webapp/src/layout/sass/_timeline.scss
@@ -142,6 +142,9 @@
 
     .week-changer {
       margin-top: 0.25rem;
+      width: 90%;
+      display: flex;
+      justify-content: space-between;
 
       label {
         padding: 0 0.25rem;
diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/components/toolbar/DateTimeInfo.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/components/toolbar/DateTimeInfo.js
index 6c4f2faa40a..0ce9a61aa31 100644
--- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/components/toolbar/DateTimeInfo.js
+++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/components/toolbar/DateTimeInfo.js
@@ -20,7 +20,6 @@ export default function DateTimeInfo() {
     useEffect(() => { // initializer
         if (!currentUTC) {
             async function fetchUTC() {
-                console.log("fetching utc")
                 let utc = await UtilService.getUTC()
                 setCurrentUTC(moment(utc))
             }
@@ -31,7 +30,6 @@ export default function DateTimeInfo() {
 
     useEffect(() => { //get LST after current utc is retrieved
         async function fetchLST(currentUTC) {
-            console.log("fetching lst")
             let lst = await UtilService.getLST(currentUTC.format(UIConstants.UTC_DATE_TIME_FORMAT))
             const hoursMinutesSeconds = lst.split(".")[0].split(":").map(stringTimeElement => parseInt(stringTimeElement))
             setCurrentLST(moment().set({
diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/components/toolbar/DateTimeNavigator.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/components/toolbar/DateTimeNavigator.js
index 8c58145ccae..e90ad88420b 100644
--- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/components/toolbar/DateTimeNavigator.js
+++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/components/toolbar/DateTimeNavigator.js
@@ -1,30 +1,20 @@
-import {Checkbox} from "primereact/checkbox";
 import Flatpickr from "react-flatpickr";
 import {Button} from "primereact/button";
-import {Dropdown} from "primereact/dropdown";
-import React, {useState} from "react";
+import React from "react";
 import DateTimeInfo from "./DateTimeInfo";
-function getMoveWithTimeCheckbox(allowLive, isLive, setIsLive) { //TODO: make sure it updates the timeline
-    if (!allowLive || !setIsLive) {
-        return null
-    }
-    return <div className="p-col-1 timeline-filters" style={{paddingTop: "5px"}}>
-        <Checkbox checked={isLive} onChange={(e) => setIsLive(e.checked)}
-                  title="Move the timeline every 5 seconds a little to the right" style={{paddingRight: "3px"}}/>
-        <label style={{marginBottom: "0px"}}>Move with time</label>
-    </div>
-}
-
+import moment from "moment";
+import UIConstants from "../../../../utils/ui.constants";
 
-function getDateSelector(isRange, currentDateValue, onChangeDateCallback, onCloseDateCallback, onClickDateResetButtonCallback) {
-    if (!currentDateValue || !onCloseDateCallback || !onClickDateResetButtonCallback) {
+function getDateSelector(isRange, startTime, setStartAndEndTimeCallback) {
+    if (!startTime) {
         return null;
     }
-    const title = isRange ? "Showing Date Range" : "Showing week of";
+    const title = isRange ? "Showing Date Range" : "Showing week from";
     const mode = isRange ? "range" : "single"
     const options = {
         "allowInput": true,
         "enableSeconds": isRange,
+        "enableTime": isRange,
         "time_24hr": isRange,
         "mode": mode,
         "minuteIncrement": 1,
@@ -35,21 +25,32 @@ function getDateSelector(isRange, currentDateValue, onChangeDateCallback, onClos
     const calenderButtonTitle = isRange ? "Click to change the date range" : "Click to select the date of the week"
     const resetButtonTitle = isRange ? "Reset to the default date range" : "Reset to the current week"
 
-    return <div>
+    return <div data-testid="nav-datetime-selector">
         <label>{title}</label>
         <Flatpickr data-enable-time={isRange}
                    data-input
-                   value={currentDateValue}
+                   value={startTime.format(UIConstants.CALENDAR_DEFAULTDATE_FORMAT)}
                    options={options}
-                   onChange={value => onChangeDateCallback(value)}
-                   onClose={value => onCloseDateCallback(value)}>
+                   onClose={value => {
+                       const chosenDate = moment(value[0]);
+                       const newStartTime = moment(chosenDate.startOf('day'))
+                       const newEndTime = moment(chosenDate.add(UIConstants.WEEKVIEW_SHOW_NUMBER_OF_DAYS, 'days').endOf('day'))
+                       setStartAndEndTimeCallback(newStartTime, newEndTime)
+
+                   }}>
             <input type="text" data-input className="p-inputtext p-component calendar-input"/>
             <button className="p-button p-component p-button-icon-only calendar-button"
-                    title={calenderButtonTitle} data-toggle>
+                    title={calenderButtonTitle} data-toggle data-testid="nav-calendar-button">
                 <i className="fas fa-calendar"></i>
             </button>
             <button className="p-button p-component p-button-icon-only calendar-reset"
-                    onClick={() => onClickDateResetButtonCallback()}
+                    data-testid="nav-reset-button"
+                    onClick={() => {
+                        const now = moment(moment.now())
+                        const newStartTime = moment(now.startOf('day'));
+                        const newEndTime = moment(now.add(UIConstants.WEEKVIEW_SHOW_NUMBER_OF_DAYS, 'days').endOf('day'));
+                        setStartAndEndTimeCallback(newStartTime, newEndTime)
+                    }}
                     title={resetButtonTitle}>
                 <i className="fas fa-sync-alt"></i>
             </button>
@@ -57,126 +58,53 @@ function getDateSelector(isRange, currentDateValue, onChangeDateCallback, onClos
     </div>
 }
 
-function getWeekChanger(isRange, onWeekChangeCallback) {
-    if (isRange || !onWeekChangeCallback) {
+function getWeekChanger(isRange, startTime, endTime, setStartAndEndTimeCallback) {
+    if (isRange || startTime === undefined || endTime === undefined) {
         return null
     }
-    return <div className="week-changer">
-        <Button icon="pi pi-angle-double-left" title="Previous Week" onClick={() => onWeekChangeCallback(-1)}/>
-        <label>Week</label>
-        <Button icon="pi pi-angle-double-right" title="Next Week" onClick={() => onWeekChangeCallback(1)}/>
-    </div>
-}
+    const weekNumbers = startTime.week() === endTime.week() ? startTime.week() : startTime.week() + "&" + endTime.week()
 
-function getResetButton(onClickTimeZoomResetButtonCallback) {
-    return <div>
-        <label>Reset:</label>
-        <div>
-            <Button icon="pi pi-undo"
-                    className="p-button p-button-primary"
-                    onClick={onClickTimeZoomResetButtonCallback}
-                    title="Reset Zoom & Move to Current Time"/>
-        </div>
-    </div>
-}
+    return <div className="week-changer" data-testid="nav-week-changer">
+        <Button data-testid="nav-week-previous" icon="pi pi-angle-double-left" title="Previous 7 days" onClick={() => {
+            const newStartTime = moment(startTime).subtract(1, 'weeks');
+            const newEndTIme = moment(endTime).subtract(1, 'weeks');
+            setStartAndEndTimeCallback(newStartTime, newEndTIme)
 
-function getIconButton(title, onClickCallback, iconClassName, disabled = false) {
-    return <button className="p-link"
-                   title={title}
-                   onClick={onClickCallback}
-                   disabled={disabled}>
-        <i className={`pi ${iconClassName}`}/>
-    </button>
-}
-
-function getZoomAndMoveActions(moveLeftCallback, moveRightCallback, zoomOutCallback, zoomInCallback, zoomLevel = "") {
-    return <div className="move-container">
-        <label>Move:</label>
-        <div>
-            {getIconButton("Move Left", moveLeftCallback, "pi-angle-left")}
-            {getIconButton("Zoom out", zoomOutCallback, "pi-minus-circle", zoomLevel.startsWith("Custom"))}
-            {getIconButton("Zoom in", zoomInCallback, "pi-plus-circle", zoomLevel.startsWith("Custom"))}
-            {getIconButton("Move Right", moveRightCallback, "pi-angle-right")}
-        </div>
+        }}/>
+        <label> Week <span>{weekNumbers}</span> </label>
+        <Button data-testid="nav-week-next" icon="pi pi-angle-double-right" title="Next 7 days" onClick={() => {
+            const newStartTime = moment(startTime).add(1, 'weeks');
+            const newEndTIme = moment(endTime).add(1, 'weeks');
+            setStartAndEndTimeCallback(newStartTime, newEndTIme)
+        }}/>
     </div>
 }
 
-function getZoomSelect(zoomLevel, allZoomLevels, onChangeZoomLevelCallback) {
-    return <div className="zoom-selector-container">
-        <label>Span:</label>
-        <div>
-            <Dropdown optionLabel="name" optionValue="name"
-                      value={zoomLevel}
-                      options={allZoomLevels}
-                      filter
-                      showClear={false}
-                      filterBy="name"
-                      onChange={(e) => onChangeZoomLevelCallback(e.value, false)}
-                      placeholder="Zoom"/>
-        </div>
-    </div>
+function changeStartAndEndTimeCallback(setStartTime, setEndTime) {
+    return (newStartTime, newEndTime) => {
+        setStartTime(newStartTime);
+        setEndTime(newEndTime);
+    };
 }
 
 export default function DateTimeNavigator(props) {
     const {
-        allowLive,
-        timelineStore,
-        timelineCommonUtils,
-        isRange,
-        currentDateValue,
-        onChangeDateCallback,
-        onCloseDateCallback,
-        onClickDateResetButtonCallback,
-        onWeekChangeCallback,
-        onClickTimeZoomResetButtonCallback,
-        moveLeftCallback,
-        moveRightCallback,
-        zoomOutCallback,
-        zoomInCallback,
-        zoomLevel,
-        allZoomLevels,
-        onChangeZoomLevelCallback
+        startTime,
+        endTime,
+        setStartTime,
+        setEndTime
     } = props
 
-    const [isLive, setIsLive] = useState(false)
-
-    // setInterval(() => { //TODO: fix clock
-    //     if (currentUTC && currentLST) {
-    //         console.log("updating times", currentLST, currentUTC)
-    //         setCurrentUTC(currentUTC.add(1, 'seconds'));
-    //         setCurrentLST(currentLST.add(1, 'seconds'));
-    //     }
-    // }, 1000);
-
-
-    //
-    // useEffect(() => {
-    //     if (timelineStore) {
-    //         isLive ? timelineStore.isLive = isLive : delete timelineStore["isLive"];
-    //         timelineCommonUtils.storeUIAttributes(timelineStore);
-    //     }
-    // }, [isLive])
-
-
-    return <div className=" p-grid timeline-datetime-navigator">
+    return <div className="p-grid timeline-datetime-navigator">
         <div className="section">
             <div className="header">Navigation</div>
             <div className="group">
                 <DateTimeInfo/>
-                {/*<div className=" selector-container">*/}
-                {/*    {getDateSelector(isRange, currentDateValue, onChangeDateCallback, onCloseDateCallback, onClickDateResetButtonCallback)}*/}
-                {/*    {getMoveWithTimeCheckbox(allowLive, isLive, setIsLive)}*/}
-                {/*    {getWeekChanger(isRange, onWeekChangeCallback)}*/}
-                {/*</div>*/}
+                <div className="selector-container">
+                    {getDateSelector(false, startTime, changeStartAndEndTimeCallback(setStartTime, setEndTime))}
+                    {getWeekChanger(false, startTime, endTime, changeStartAndEndTimeCallback(setStartTime, setEndTime))}
+                </div>
             </div>
         </div>
-        {/*<div className=" section">*/}
-        {/*    <div className=" header">Zoom</div>*/}
-        {/*    <div className=" group group--row">*/}
-        {/*        {getZoomSelect(zoomLevel, allZoomLevels, onChangeZoomLevelCallback)}*/}
-        {/*        {getZoomAndMoveActions(moveLeftCallback, moveRightCallback, zoomOutCallback, zoomInCallback, zoomLevel)}*/}
-        {/*        {getResetButton(onClickTimeZoomResetButtonCallback)}*/}
-        {/*    </div>*/}
-        {/*</div>*/}
     </div>
 }
\ No newline at end of file
diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/components/toolbar/DateTimeNavigator.test.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/components/toolbar/DateTimeNavigator.test.js
new file mode 100644
index 00000000000..8b753425938
--- /dev/null
+++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/components/toolbar/DateTimeNavigator.test.js
@@ -0,0 +1,83 @@
+import moment from "moment";
+import DateTimeNavigator from "./DateTimeNavigator";
+import {render} from "@testing-library/react";
+import {clickItem, removeReact18ConsoleErrors} from "../../../../utils/test.helper";
+
+removeReact18ConsoleErrors()
+
+describe('DateTimeNavigator', () => {
+    const mockSetStartTime = jest.fn();
+    const mockSetEndTime = jest.fn();
+
+    it('renders date selector and week changer correctly', () => {
+        const pageContent = render(
+            <DateTimeNavigator
+                startTime={moment()}
+                endTime={moment()}
+                setStartTime={mockSetStartTime}
+                setEndTime={mockSetEndTime}
+            />
+        );
+
+        const dateSelector = pageContent.getByTestId('nav-datetime-selector');
+        const dateSelectorInput = pageContent.getByTestId('nav-calendar-button');
+        const resetButton = pageContent.getByTestId('nav-reset-button');
+        const nextWeekButton = pageContent.getByTestId('nav-week-next');
+        const previousWeekButton = pageContent.getByTestId('nav-week-previous');
+
+        expect(dateSelector).toBeInTheDocument();
+        expect(dateSelector.textContent).toBe("Showing week from");
+        expect(dateSelectorInput).toBeInTheDocument();
+        expect(dateSelectorInput.title).toBe("Click to select the date of the week");
+        expect(resetButton).toBeInTheDocument();
+        expect(resetButton.title).toBe("Reset to the current week");
+        expect(nextWeekButton).toBeInTheDocument();
+        expect(nextWeekButton.title).toBe("Next 7 days");
+        expect(previousWeekButton).toBeInTheDocument();
+        expect(previousWeekButton.title).toBe("Previous 7 days");
+    });
+
+    it('Clicks buttons; reset, previous+next week', () => {
+        const mockStartTime = moment('2023-08-15');
+        const mockEndTime = moment('2023-08-22');
+        const pageContent = render(
+            <DateTimeNavigator
+                startTime={mockStartTime}
+                endTime={mockEndTime}
+                setStartTime={mockSetStartTime}
+                setEndTime={mockSetEndTime}
+            />
+        );
+
+        const resetButton = pageContent.getByTestId('nav-reset-button');
+        const nextWeekButton = pageContent.getByTestId('nav-week-next');
+        const previousWeekButton = pageContent.getByTestId('nav-week-previous');
+
+
+        clickItem(resetButton)
+        expect(mockSetStartTime).toHaveBeenCalledTimes(1);
+        expect(mockSetEndTime).toHaveBeenCalledTimes(1);
+
+        clickItem(previousWeekButton)
+        expect(mockSetStartTime).toHaveBeenCalledTimes(2);
+        expect(mockSetEndTime).toHaveBeenCalledTimes(2);
+
+        clickItem(nextWeekButton)
+        expect(mockSetStartTime).toHaveBeenCalledTimes(3);
+        expect(mockSetEndTime).toHaveBeenCalledTimes(3);
+    });
+
+    it('does not render week changer or date selector when startTime is not provided', () => {
+        const pageContent = render(
+            <DateTimeNavigator
+                startTime={undefined}
+                endTime={moment()}
+                setStartTime={mockSetStartTime}
+                setEndTime={mockSetEndTime}
+            />
+        );
+
+        expect(pageContent.queryByTestId('nav-datetime-selector')).not.toBeInTheDocument()
+        expect(pageContent.queryByTestId('nav-week-changer')).not.toBeInTheDocument()
+    });
+});
\ No newline at end of file
diff --git a/SAS/TMSS/frontend/tmss_webapp/src/utils/ui.constants.js b/SAS/TMSS/frontend/tmss_webapp/src/utils/ui.constants.js
index 837f81ca6bb..5c223425ced 100644
--- a/SAS/TMSS/frontend/tmss_webapp/src/utils/ui.constants.js
+++ b/SAS/TMSS/frontend/tmss_webapp/src/utils/ui.constants.js
@@ -72,6 +72,7 @@ const UIConstants = {
     SU_NOT_STARTED_STATUSES: ['defined', 'schedulable', 'scheduled', 'unschedulable'],
     SU_ACTIVE_STATUSES: ['started', 'observing', 'observed', 'processing', 'processed', 'ingesting', 'ingested'],
     STORE_KEY_TIMELINE: "TIMELINE_UI_ATTR",
+    WEEKVIEW_SHOW_NUMBER_OF_DAYS: 6,
     ALL_ZOOM_LEVELS_WEEK: [{name: '30 Minutes', days: 0, hours: 0, minutes: 30},
         {name: '1 Hour', days: 0, hours: 1, minutes: 0},
         {name: '3 Hours', days: 0, hours: 3, minutes: 0},
-- 
GitLab