Skip to content

Commit 2ce684d

Browse files
committed
fix: tanstack and more
1 parent 36173ca commit 2ce684d

4 files changed

Lines changed: 32 additions & 49 deletions

File tree

frontend/src/__test__/screens/CreateOpening.test.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ describe('CreateOpening screen', () => {
4343
});
4444

4545
it('shows insufficient privileges notification when user lacks privilege', () => {
46-
vi.mocked(famUtils.hasCreateOpeningPriviledge).mockReturnValue(false);
46+
vi.mocked(famUtils.hasCreateOpeningPrivilege).mockReturnValue(false);
4747
mockUseAuth.mockReturnValue({ user: { privileges: {}, associatedClients: [] } });
4848
mockUseSearchParams.mockReturnValue([new URLSearchParams([['type', TENURED_OPENING]]), vi.fn()]);
4949

@@ -55,7 +55,7 @@ describe('CreateOpening screen', () => {
5555
});
5656

5757
it('shows feature unavailable for government funded opening type', () => {
58-
vi.mocked(famUtils.hasCreateOpeningPriviledge).mockReturnValue(true);
58+
vi.mocked(famUtils.hasCreateOpeningPrivilege).mockReturnValue(true);
5959
mockUseAuth.mockReturnValue({ user: { privileges: {}, associatedClients: [] } });
6060
mockUseSearchParams.mockReturnValue([new URLSearchParams([['type', GOV_FUNDED_OPENING]]), vi.fn()]);
6161

@@ -66,7 +66,7 @@ describe('CreateOpening screen', () => {
6666
});
6767

6868
it('navigates to / when type is invalid', () => {
69-
vi.mocked(famUtils.hasCreateOpeningPriviledge).mockReturnValue(true);
69+
vi.mocked(famUtils.hasCreateOpeningPrivilege).mockReturnValue(true);
7070
mockUseAuth.mockReturnValue({ user: { privileges: {}, associatedClients: [] } });
7171
mockUseSearchParams.mockReturnValue([new URLSearchParams([['type', 'invalid']]), vi.fn()]);
7272

@@ -76,7 +76,7 @@ describe('CreateOpening screen', () => {
7676
});
7777

7878
it('renders CreateOpeningForm when guards pass (tenured opening with privilege)', () => {
79-
vi.mocked(famUtils.hasCreateOpeningPriviledge).mockReturnValue(true);
79+
vi.mocked(famUtils.hasCreateOpeningPrivilege).mockReturnValue(true);
8080
mockUseAuth.mockReturnValue({ user: { privileges: { canCreateOpening: true }, associatedClients: [] } });
8181
mockUseSearchParams.mockReturnValue([new URLSearchParams([['type', TENURED_OPENING]]), vi.fn()]);
8282

frontend/src/components/MapPreview/index.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { useEffect } from "react";
22

3-
import { MapContainer, TileLayer, GeoJSON as RLGeoJSON, useMap } from "react-leaflet";
3+
import { MapContainer, TileLayer, GeoJSON as RLGeoJSON, useMap, ZoomControl } from "react-leaflet";
44
import { geoJSON } from "leaflet";
55

66
type MapPreviewProps = {
@@ -37,9 +37,10 @@ const MapPreview = ({ geojson }: MapPreviewProps) => {
3737
style={{ height: "100%", width: "100%" }}
3838
center={[49.25, -123.1]}
3939
zoom={6}
40-
zoomControl
40+
zoomControl={false}
4141
scrollWheelZoom={false}
4242
>
43+
<ZoomControl position="bottomright" />
4344
<TileLayer
4445
url="https://server.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer/tile/{z}/{y}/{x}"
4546
attribution="Tiles &copy; Esri &mdash; Esri, DeLorme, NAVTEQ, TomTom, Intermap, iPC, USGS, FAO, NPS, NRCAN, GeoBase, Kadaster NL, Ordnance Survey, Esri Japan, METI, Esri China (Hong Kong), and the GIS User Community"

frontend/src/constants/tanstackConfig.ts

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -102,15 +102,13 @@ function errorHandler(
102102
// Prevent the query from remaining in errored state while we attempt token refresh.
103103
try {
104104
if (query) {
105-
// clear query error and mark as loading/fetching so UI shows a retry in-progress
106-
// 'pending' + 'fetching' = isLoading===true in TanStack Query v5
105+
// clear query error and mark as retry in-progress without forcing internal fetchStatus
106+
// Partial state updates are supported in TanStack Query v5.
107107
// eslint-disable-next-line @typescript-eslint/no-explicit-any
108-
(query as any).setState?.((oldState: any) => ({
109-
...oldState,
108+
(query as any).setState?.({
110109
error: null,
111-
status: 'pending',
112-
fetchStatus: 'fetching'
113-
}));
110+
status: 'pending'
111+
});
114112
}
115113
} catch (e) {
116114
// ignore if we can't touch internal state

frontend/src/screens/CreateOpening/CreateOpeningForm.tsx

Lines changed: 20 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { FormEvent, useCallback, useEffect, useRef, useState } from 'react';
22
import { useMutation } from '@tanstack/react-query';
3-
import { Button, Column, Form, Grid, InlineNotification, Loading, Modal, Stack } from '@carbon/react';
3+
import { Button, Column, Form, Grid, InlineNotification, Loading, Stack } from '@carbon/react';
44
import { useBlocker, useNavigate } from 'react-router-dom';
55
import { ArrowRight, TrashCan } from '@carbon/icons-react';
66
import { scrollToSection } from '@/utils/InputUtils';
@@ -28,7 +28,7 @@ interface CreateOpeningFormProps {
2828

2929
export const CreateOpeningForm = ({ type, currentStep, setCurrentStep }: CreateOpeningFormProps) => {
3030
// All hooks called unconditionally at component top
31-
const [isCancelModalOpen, setIsCancelModalOpen] = useState<boolean>(false);
31+
const [isLeavePageModalOpen, setIsLeavePageModalOpen] = useState<boolean>(false);
3232
const navigate = useNavigate();
3333
const [form, setForm] = useState<CreateOpeningFormType>(() => {
3434
return structuredClone(DefaultOpeningForm);
@@ -215,16 +215,30 @@ export const CreateOpeningForm = ({ type, currentStep, setCurrentStep }: CreateO
215215
}
216216

217217
const handleCancel = () => {
218-
setIsCancelModalOpen(true);
218+
if (!isNavigationBlocked) {
219+
bypassBlockerRef.current = true;
220+
navigate(OpeningsRoute.path!);
221+
return;
222+
}
223+
224+
setIsLeavePageModalOpen(true);
219225
};
220226

221227
const handleLeaveConfirm = () => {
222228
bypassBlockerRef.current = true;
223-
blocker.proceed();
229+
setIsLeavePageModalOpen(false);
230+
231+
if (blocker.state === 'blocked') {
232+
blocker?.proceed?.();
233+
return;
234+
}
235+
236+
navigate(OpeningsRoute.path!);
224237
};
225238

226239
const handleStay = () => {
227-
blocker.reset();
240+
setIsLeavePageModalOpen(false);
241+
blocker?.reset?.();
228242
};
229243

230244
const handleCreate = () => {
@@ -325,38 +339,8 @@ export const CreateOpeningForm = ({ type, currentStep, setCurrentStep }: CreateO
325339
</Grid>
326340
</Column>
327341

328-
<Modal
329-
passiveModal
330-
danger
331-
open={isCancelModalOpen}
332-
modalHeading={<ModalHead title="Are you sure you want to cancel?" helperTop="Create new opening" />}
333-
onRequestClose={() => setIsCancelModalOpen(false)}
334-
className="default-modal"
335-
preventCloseOnClickOutside
336-
size="sm"
337-
>
338-
<Grid>
339-
<Column sm={4} md={8} lg={16}>
340-
<p className='cancel-content'>
341-
If you leave this page, all the information you've entered will be lost.
342-
</p>
343-
</Column>
344-
<Column sm={4} md={8} lg={16}>
345-
<Stack orientation="horizontal" gap={2} className="default-equal-split-stack">
346-
<Button className="modal-button" kind="secondary" onClick={() => setIsCancelModalOpen(false)}>
347-
Continue reviewing
348-
</Button>
349-
350-
<Button className="modal-button" kind="danger" renderIcon={TrashCan} onClick={() => { bypassBlockerRef.current = true; navigate(OpeningsRoute.path!); }}>
351-
Leave without saving
352-
</Button>
353-
</Stack>
354-
</Column>
355-
</Grid>
356-
</Modal>
357-
358342
<LeavePageModal
359-
open={blocker.state === 'blocked'}
343+
open={isLeavePageModalOpen || blocker.state === 'blocked'}
360344
onRequestClose={handleStay}
361345
onLeave={handleLeaveConfirm}
362346
onStay={handleStay}

0 commit comments

Comments
 (0)