From 3537973f825e08d0d14ac6dd89947eed71e761ab Mon Sep 17 00:00:00 2001 From: Luke Vincent <11511477+lincent@users.noreply.github.com> Date: Wed, 21 Aug 2024 14:36:01 +0100 Subject: [PATCH] fix infinite API calls --- .../src/components/header/Toolbar.spec.tsx | 43 ++++-- .../src/components/header/Toolbar.tsx | 22 ++- .../single-city/SingleCity.spec.tsx | 8 +- .../src/components/single-city/SingleCity.tsx | 33 ++--- .../AverageComparisonChart.spec.tsx | 8 +- .../AverageComparisonChart.tsx | 20 +-- .../SiteMeasurementsChart.spec.tsx | 8 +- .../SiteMeasurementsChart.tsx | 19 +-- .../summary-view/GlobalSummary.spec.tsx | 7 +- .../components/summary-view/GlobalSummary.tsx | 49 ++++--- .../summary-view/SummaryViewHeader.spec.tsx | 10 +- .../summary-view/SummaryViewHeader.tsx | 6 +- air-quality-ui/src/context.tsx | 126 +++++++----------- 13 files changed, 193 insertions(+), 166 deletions(-) diff --git a/air-quality-ui/src/components/header/Toolbar.spec.tsx b/air-quality-ui/src/components/header/Toolbar.spec.tsx index ac7fe5b9..14d8b840 100644 --- a/air-quality-ui/src/components/header/Toolbar.spec.tsx +++ b/air-quality-ui/src/components/header/Toolbar.spec.tsx @@ -8,21 +8,24 @@ import { ForecastWindowSelectorProps, } from './ForecastWindowSelector' import { Toolbar } from './Toolbar' +import { SetForcastDetailsType } from '../../context' -const mockSetForecastBaseDate: (val: DateTime) => void = jest.fn() -const mockSetMaxForecastDate: (val: number) => void = jest.fn() -const mockSetMaxInSituDate: (val: number) => void = jest.fn() +const mockSetDetails: (val: SetForcastDetailsType) => void = jest.fn() const dateNow = DateTime.fromISO('2024-06-01T12:00:00', { zone: 'UTC' }) Settings.now = () => dateNow.toMillis() jest.mock('../../context', () => ({ useForecastContext: jest.fn().mockReturnValue({ - forecastBaseDate: DateTime.fromISO('2024-06-01T12:00:00', { zone: 'UTC' }), - maxInSituDate: DateTime.fromISO('2024-06-10T09:00:00', { zone: 'utc' }), - setForecastBaseDate: (x: DateTime) => mockSetForecastBaseDate(x), - setMaxForecastDate: (x: number) => mockSetMaxForecastDate(x), - setMaxInSituDate: (x: number) => mockSetMaxInSituDate(x), + forecastDetails: { + forecastBaseDate: DateTime.fromISO('2024-06-01T12:00:00', { + zone: 'UTC', + }), + maxMeasurementDate: DateTime.fromISO('2024-06-10T09:00:00', { + zone: 'utc', + }), + }, + setDetails: (x: SetForcastDetailsType) => mockSetDetails(x), }), })) @@ -58,6 +61,9 @@ jest.mock('./ForecastWindowSelector', () => ({ })) describe('Toolbar component', () => { + beforeEach(() => { + jest.clearAllMocks() + }) it('renders toolbar', () => { render() expect(screen.getByRole('toolbar')).toBeInTheDocument() @@ -85,9 +91,12 @@ describe('Toolbar component', () => { }) fireEvent.click(screen.getByText('Ok')) await waitFor(() => { - expect(mockSetForecastBaseDate).toHaveBeenCalledWith( - DateTime.fromISO('2024-06-10T09:00:00', { zone: 'utc' }), - ) + expect(mockSetDetails).toHaveBeenCalledWith({ + forecastBaseDate: DateTime.fromISO('2024-06-10T09:00:00', { + zone: 'utc', + }), + forecastWindow: 1, + }) }) }) it('Setting invalid date and clicking button does not update context', async () => { @@ -102,7 +111,7 @@ describe('Toolbar component', () => { }) fireEvent.click(screen.getByText('Ok')) await waitFor(() => { - expect(mockSetForecastBaseDate).not.toHaveBeenCalledWith( + expect(mockSetDetails).not.toHaveBeenCalledWith( DateTime.fromISO('2024-08-10T09:00:00', { zone: 'utc' }), ) }) @@ -130,7 +139,7 @@ describe('Toolbar component', () => { expected: 5, }, ].forEach(({ testData, expected }) => { - it(`When forecast window selector is changed to ${testData}, when the ok button is clicked, selectedForecastWindow is ${expected}`, async () => { + it(`When forecast window selector is changed to ${testData.label}, when the ok button is clicked, selectedForecastWindow is ${expected}`, async () => { render() const beforeSetting = selectedForecastWindow await waitFor(() => { @@ -140,8 +149,14 @@ describe('Toolbar component', () => { await waitFor(() => { expect(beforeSetting).toStrictEqual({ value: 1, label: '1' }) }) + const expects = { + forecastBaseDate: DateTime.fromISO('2024-06-01T12:00:00', { + zone: 'utc', + }), + forecastWindow: expected, + } await waitFor(() => { - expect(mockSetMaxForecastDate).toHaveBeenCalledWith(expected) + expect(mockSetDetails).toHaveBeenCalledWith(expects) }) }) }) diff --git a/air-quality-ui/src/components/header/Toolbar.tsx b/air-quality-ui/src/components/header/Toolbar.tsx index 640343f9..09d5c789 100644 --- a/air-quality-ui/src/components/header/Toolbar.tsx +++ b/air-quality-ui/src/components/header/Toolbar.tsx @@ -12,14 +12,11 @@ import { useForecastContext } from '../../context' import { VAirifyButton } from '../common/button/VAirifyButton' export const Toolbar = () => { - const { - forecastBaseDate, - setForecastBaseDate, - setMaxForecastDate, - setMaxInSituDate, - } = useForecastContext() - const [selectedForecastBaseDate, setSelectedForecastBaseDate] = - useState>(forecastBaseDate) + const { forecastDetails, setDetails } = useForecastContext() + + const [selectedForecastBaseDate, setSelectedForecastBaseDate] = useState< + DateTime + >(forecastDetails.forecastBaseDate) const [selectedForecastWindow, setSelectedForecastWindow] = useState({ value: 1, label: '1' }) const [isInvalidDateTime, setIsInvalidDateTime] = useState(false) @@ -37,7 +34,7 @@ export const Toolbar = () => {
@@ -50,10 +47,11 @@ export const Toolbar = () => { { if (selectedForecastBaseDate) { - setForecastBaseDate(selectedForecastBaseDate) + setDetails({ + forecastBaseDate: selectedForecastBaseDate, + forecastWindow: selectedForecastWindow.value, + }) } - setMaxForecastDate(selectedForecastWindow.value) - setMaxInSituDate(selectedForecastWindow.value) }} text={'Ok'} isButtonDisabled={isInvalidDateTime} diff --git a/air-quality-ui/src/components/single-city/SingleCity.spec.tsx b/air-quality-ui/src/components/single-city/SingleCity.spec.tsx index 171af4de..3f06dce8 100644 --- a/air-quality-ui/src/components/single-city/SingleCity.spec.tsx +++ b/air-quality-ui/src/components/single-city/SingleCity.spec.tsx @@ -16,9 +16,11 @@ jest.mock('@tanstack/react-query', () => ({ jest.mock('../../context', () => ({ useForecastContext: jest.fn().mockReturnValue({ - forecastBaseDate: DateTime.now(), - maxInSituDate: DateTime.now(), - maxForecastDate: DateTime.now(), + forecastDetails: { + forecastBaseDate: DateTime.now(), + maxMeasurementDate: DateTime.now(), + maxForecastDate: DateTime.now(), + }, }), })) diff --git a/air-quality-ui/src/components/single-city/SingleCity.tsx b/air-quality-ui/src/components/single-city/SingleCity.tsx index 9f1a4e05..bcf3306a 100644 --- a/air-quality-ui/src/components/single-city/SingleCity.tsx +++ b/air-quality-ui/src/components/single-city/SingleCity.tsx @@ -25,8 +25,8 @@ const getSiteName = (measurement: MeasurementsResponseDto): string => { } export const SingleCity = () => { - const { forecastBaseDate, maxInSituDate, maxForecastDate } = - useForecastContext() + const { forecastDetails } = useForecastContext() + const { name: locationName = '' } = useParams() const [ { @@ -43,16 +43,16 @@ export const SingleCity = () => { queries: [ { queryKey: [ - forecastBaseDate, + forecastDetails.forecastBaseDate, locationName, - maxInSituDate, - maxForecastDate, + forecastDetails.maxMeasurementDate, + forecastDetails.maxForecastDate, ], queryFn: () => getForecastData( - forecastBaseDate, - maxForecastDate, - forecastBaseDate, + forecastDetails.forecastBaseDate, + forecastDetails.maxForecastDate, + forecastDetails.forecastBaseDate, locationName, ), }, @@ -60,14 +60,17 @@ export const SingleCity = () => { queryKey: [ 'measurements', locationName, - forecastBaseDate, - maxInSituDate, - maxForecastDate, + forecastDetails.forecastBaseDate, + forecastDetails.maxMeasurementDate, + forecastDetails.maxForecastDate, ], queryFn: () => - getMeasurements(forecastBaseDate, maxInSituDate, 'city', [ - locationName, - ]), + getMeasurements( + forecastDetails.forecastBaseDate, + forecastDetails.maxMeasurementDate, + 'city', + [locationName], + ), }, ], }) @@ -201,7 +204,7 @@ export const SingleCity = () => { cityName={locationName} forecastData={forecastData} measurementsData={measurements} - forecastBaseTime={forecastBaseDate} + forecastBaseTime={forecastDetails.forecastBaseDate} />
{measurementsByPollutantBySite && diff --git a/air-quality-ui/src/components/single-city/average-comparison-chart/AverageComparisonChart.spec.tsx b/air-quality-ui/src/components/single-city/average-comparison-chart/AverageComparisonChart.spec.tsx index 0ad50968..d036a66b 100644 --- a/air-quality-ui/src/components/single-city/average-comparison-chart/AverageComparisonChart.spec.tsx +++ b/air-quality-ui/src/components/single-city/average-comparison-chart/AverageComparisonChart.spec.tsx @@ -12,9 +12,11 @@ jest.mock('echarts-for-react', () => () =>
Mock Chart
) jest.mock('../../../context', () => ({ useForecastContext: jest.fn().mockReturnValue({ - forecastBaseDate: DateTime.now(), - maxInSituDate: DateTime.now(), - maxForecastDate: DateTime.now(), + forecastDetails: { + forecastBaseDate: DateTime.now(), + maxMeasurementDate: DateTime.now(), + maxForecastDate: DateTime.now(), + }, }), })) diff --git a/air-quality-ui/src/components/single-city/average-comparison-chart/AverageComparisonChart.tsx b/air-quality-ui/src/components/single-city/average-comparison-chart/AverageComparisonChart.tsx index cf8e1993..74a6b508 100644 --- a/air-quality-ui/src/components/single-city/average-comparison-chart/AverageComparisonChart.tsx +++ b/air-quality-ui/src/components/single-city/average-comparison-chart/AverageComparisonChart.tsx @@ -30,8 +30,8 @@ export const AverageComparisonChart = ( props: AverageComparisonChartProps, ): JSX.Element => { const chartRef = useRef(null) - const { forecastBaseDate, maxForecastDate, maxInSituDate } = - useForecastContext() + + const { forecastDetails } = useForecastContext() const measurementsAveragedData = useMemo(() => { if (props.measurementsData) { @@ -44,15 +44,15 @@ export const AverageComparisonChart = ( }, [props.measurementsData, props.forecastBaseTime]) const zoomPercent = getInSituPercentage( - forecastBaseDate, - maxForecastDate, - maxInSituDate, + forecastDetails.forecastBaseDate, + forecastDetails.maxForecastDate, + forecastDetails.maxMeasurementDate, ) const zoomEventHandler = useCallback(() => { const instance = chartRef.current?.getEchartsInstance() - updateChartSubtext(instance!, forecastBaseDate) - }, [forecastBaseDate]) + updateChartSubtext(instance!, forecastDetails.forecastBaseDate) + }, [forecastDetails]) return ( () =>
Mock Chart
) jest.mock('../../../context', () => ({ useForecastContext: jest.fn().mockReturnValue({ - forecastBaseDate: DateTime.now(), - maxInSituDate: DateTime.now(), - maxForecastDate: DateTime.now(), + forecastDetails: { + forecastBaseDate: DateTime.now(), + maxMeasurementDate: DateTime.now(), + maxForecastDate: DateTime.now(), + }, }), })) diff --git a/air-quality-ui/src/components/single-city/site-measurement-chart/SiteMeasurementsChart.tsx b/air-quality-ui/src/components/single-city/site-measurement-chart/SiteMeasurementsChart.tsx index ab49093f..5f77cf1a 100644 --- a/air-quality-ui/src/components/single-city/site-measurement-chart/SiteMeasurementsChart.tsx +++ b/air-quality-ui/src/components/single-city/site-measurement-chart/SiteMeasurementsChart.tsx @@ -34,8 +34,7 @@ export const SiteMeasurementsChart = ({ }: SiteMeasurementsChartProps): JSX.Element => { const chartRef = useRef(null) - const { forecastBaseDate, maxForecastDate, maxInSituDate } = - useForecastContext() + const { forecastDetails } = useForecastContext() const eChartEventHandler = useCallback( ({ @@ -60,15 +59,15 @@ export const SiteMeasurementsChart = ({ ) const zoomPercent = getInSituPercentage( - forecastBaseDate, - maxForecastDate, - maxInSituDate, + forecastDetails.forecastBaseDate, + forecastDetails.maxForecastDate, + forecastDetails.maxMeasurementDate, ) const zoomEventHandler = useCallback(() => { const instance = chartRef.current?.getEchartsInstance() - updateChartSubtext(instance!, forecastBaseDate) - }, [forecastBaseDate]) + updateChartSubtext(instance!, forecastDetails.forecastBaseDate) + }, [forecastDetails]) return ( <> @@ -85,7 +84,11 @@ export const SiteMeasurementsChart = ({ zoomPercent, measurementsBySite, forecastData, - createSubtext(forecastBaseDate, forecastBaseDate, maxInSituDate), + createSubtext( + forecastDetails.forecastBaseDate, + forecastDetails.forecastBaseDate, + forecastDetails.maxMeasurementDate, + ), cityName, seriesColorsBySite, )} diff --git a/air-quality-ui/src/components/summary-view/GlobalSummary.spec.tsx b/air-quality-ui/src/components/summary-view/GlobalSummary.spec.tsx index effc8514..1258805f 100644 --- a/air-quality-ui/src/components/summary-view/GlobalSummary.spec.tsx +++ b/air-quality-ui/src/components/summary-view/GlobalSummary.spec.tsx @@ -14,8 +14,11 @@ jest.mock('@tanstack/react-query', () => ({ jest.mock('../../context', () => ({ useForecastContext: jest.fn().mockReturnValue({ - forecastBaseDate: DateTime.now(), - maxInSituDate: DateTime.now(), + forecastDetails: { + forecastBaseDate: DateTime.now(), + maxMeasurementDate: DateTime.now(), + maxForecastDate: DateTime.now(), + }, }), })) diff --git a/air-quality-ui/src/components/summary-view/GlobalSummary.tsx b/air-quality-ui/src/components/summary-view/GlobalSummary.tsx index cdbafcab..8e0c3aec 100644 --- a/air-quality-ui/src/components/summary-view/GlobalSummary.tsx +++ b/air-quality-ui/src/components/summary-view/GlobalSummary.tsx @@ -18,7 +18,7 @@ import { LoadingSpinner } from '../common/LoadingSpinner' import GlobalSummaryTable from '../summary-grid/table/GlobalSummaryTable' const GlobalSummary = (): JSX.Element => { - const { forecastBaseDate, maxInSituDate } = useForecastContext() + const { forecastDetails } = useForecastContext() const [showAllColoured, setShowAllColoured] = useState(true) const wrapSetShowAllColoured = useCallback( @@ -33,27 +33,36 @@ const GlobalSummary = (): JSX.Element => { isPending: forecastPending, isError: forecastDataError, } = useQuery({ - queryKey: [forecastBaseDate, maxInSituDate], + queryKey: [ + forecastDetails.forecastBaseDate, + forecastDetails.maxMeasurementDate, + ], queryFn: () => - getForecastData(forecastBaseDate, DateTime.now(), forecastBaseDate).then( - (forecastData) => - forecastData.reduce>( - (acc, reading) => { - const location = reading.location_name - if (!acc[location]) { - acc[location] = [] - } - acc[location].push(reading) - return acc - }, - {}, - ), + getForecastData( + forecastDetails.forecastBaseDate, + DateTime.now(), + forecastDetails.forecastBaseDate, + ).then((forecastData) => + forecastData.reduce>( + (acc, reading) => { + const location = reading.location_name + if (!acc[location]) { + acc[location] = [] + } + acc[location].push(reading) + return acc + }, + {}, + ), ), }) const forecastValidTimeRange = useMemo(() => { - return getValidForecastTimesBetween(forecastBaseDate, maxInSituDate) - }, [forecastBaseDate, maxInSituDate]) + return getValidForecastTimesBetween( + forecastDetails.forecastBaseDate, + forecastDetails.maxMeasurementDate, + ) + }, [forecastDetails]) const { data: summarizedMeasurementData, @@ -61,7 +70,11 @@ const GlobalSummary = (): JSX.Element => { isError: summaryDataError, } = useQueries({ queries: forecastValidTimeRange.map((validTime) => ({ - queryKey: ['summary', validTime.toMillis(), maxInSituDate], + queryKey: [ + 'summary', + validTime.toMillis(), + forecastDetails.maxMeasurementDate, + ], queryFn: () => getMeasurementSummary(validTime), })), combine: (results) => { diff --git a/air-quality-ui/src/components/summary-view/SummaryViewHeader.spec.tsx b/air-quality-ui/src/components/summary-view/SummaryViewHeader.spec.tsx index 4351c2c4..dc31d211 100644 --- a/air-quality-ui/src/components/summary-view/SummaryViewHeader.spec.tsx +++ b/air-quality-ui/src/components/summary-view/SummaryViewHeader.spec.tsx @@ -6,8 +6,14 @@ import { SummaryViewHeader } from './SummaryViewHeader' jest.mock('../../context', () => ({ useForecastContext: jest.fn().mockReturnValue({ - forecastBaseDate: DateTime.fromISO('2024-06-01T03:00:00', { zone: 'utc' }), - maxInSituDate: DateTime.fromISO('2024-06-10T09:00:00', { zone: 'utc' }), + forecastDetails: { + forecastBaseDate: DateTime.fromISO('2024-06-01T03:00:00', { + zone: 'utc', + }), + maxMeasurementDate: DateTime.fromISO('2024-06-10T09:00:00', { + zone: 'utc', + }), + }, }), })) diff --git a/air-quality-ui/src/components/summary-view/SummaryViewHeader.tsx b/air-quality-ui/src/components/summary-view/SummaryViewHeader.tsx index 4f26e631..926d101b 100644 --- a/air-quality-ui/src/components/summary-view/SummaryViewHeader.tsx +++ b/air-quality-ui/src/components/summary-view/SummaryViewHeader.tsx @@ -12,14 +12,14 @@ export const SummaryViewHeader = ({ showAllColoured, setShowAllColoured, }: SummaryViewHeaderProps): JSX.Element => { - const { forecastBaseDate, maxInSituDate } = useForecastContext() + const { forecastDetails } = useForecastContext() return (
- Time Range: {forecastBaseDate.toFormat('dd MMM HH:mm')} + Time Range: {forecastDetails.forecastBaseDate.toFormat('dd MMM HH:mm')} {' - '} - {maxInSituDate.toFormat('dd MMM HH:mm ZZZZ')} + {forecastDetails.maxMeasurementDate.toFormat('dd MMM HH:mm ZZZZ')}