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