Skip to content

Commit 60d164b

Browse files
authored
feat: Lift and Shift to React18 (#241)
1 parent accb35b commit 60d164b

File tree

8 files changed

+4110
-6033
lines changed

8 files changed

+4110
-6033
lines changed

package-lock.json

Lines changed: 3990 additions & 5930 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -15,47 +15,47 @@
1515
},
1616
"homepage": ".",
1717
"dependencies": {
18-
"@google-cloud/bigquery": "^6.0.0",
18+
"@google-cloud/bigquery": "^7.8.0",
1919
"@react-oauth/google": "^0.12.1",
20-
"@testing-library/jest-dom": "^5.14.1",
21-
"@turf/turf": "^7.2.0",
20+
"@testing-library/jest-dom": "^6.4.6",
21+
"@turf/turf": "^6.5.0",
2222
"@vis.gl/react-google-maps": "^1.5.2",
23-
"axios": "^1.7.4",
23+
"axios": "^1.7.2",
2424
"express": "^4.19.2",
25-
"googleapis": "^89.0.0",
25+
"googleapis": "^140.0.1",
2626
"jszip": "^3.10.1",
2727
"lodash": "^4.17.21",
28-
"open": "^8.3.0",
29-
"query-string": "^7.1.0",
30-
"rc-slider": "^9.7.4",
31-
"react": "^17.0.2",
32-
"react-dom": "^17.0.2",
28+
"open": "^10.1.0",
29+
"query-string": "^9.0.0",
30+
"rc-slider": "^11.1.8",
31+
"react": "^18.3.1",
32+
"react-dom": "^18.3.1",
3333
"react-json-view": "^1.21.3",
3434
"react-scripts": "5.0.1",
35-
"react-table": "^7.7.0",
36-
"react-toastify": "^9.0.3",
35+
"react-table": "^7.8.0",
36+
"react-toastify": "^10.0.5",
3737
"react-virtualized-auto-sizer": "^1.0.24",
3838
"react-window": "^1.8.10",
3939
"s2polyline-ts": "^1.0.0",
4040
"webpack": "5.94.0",
41-
"yargs": "^17.2.1"
41+
"yargs": "^17.7.2"
4242
},
4343
"devDependencies": {
4444
"@babel/core": "^7.25.2",
4545
"@babel/eslint-parser": "^7.25.1",
4646
"@babel/plugin-transform-class-properties": "^7.24.7",
4747
"@babel/preset-react": "^7.24.7",
48-
"@typescript-eslint/eslint-plugin": "^4.29.3",
49-
"@typescript-eslint/parser": "^4.29.3",
48+
"@typescript-eslint/eslint-plugin": "^7.16.0",
49+
"@typescript-eslint/parser": "^7.16.0",
5050
"cross-env": "^7.0.3",
51-
"eslint": "^7.32.0",
52-
"eslint-config-prettier": "^8.10.0",
51+
"eslint": "^8.57.0",
52+
"eslint-config-prettier": "^9.1.0",
5353
"eslint-config-react-app": "^7.0.1",
54-
"eslint-plugin-jest": "^25.2.2",
55-
"eslint-plugin-prettier": "^4.0.0",
54+
"eslint-plugin-jest": "^28.6.0",
55+
"eslint-plugin-prettier": "^5.1.3",
5656
"eslint-plugin-react": "^7.35.0",
57-
"prettier": "^2.8.8",
58-
"typescript": "^4.4.4"
57+
"prettier": "^3.3.3",
58+
"typescript": "^5.5.3"
5959
},
6060
"scripts": {
6161
"start": "react-scripts start",
@@ -97,4 +97,4 @@
9797
"last 1 safari version"
9898
]
9999
}
100-
}
100+
}

src/App.js

Lines changed: 31 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// src/App.js
2-
import ReactDOM from "react-dom";
32
import React from "react";
3+
import { createRoot } from "react-dom/client";
44
import Map from "./Map";
55
import Dataframe from "./Dataframe";
66
import TimeSlider from "./TimeSlider";
@@ -410,7 +410,9 @@ class App extends React.Component {
410410
checkForDemoFile = async () => {
411411
try {
412412
const response = await fetch("./data.json");
413-
if (!response.ok) {
413+
const contentType = response.headers.get("content-type");
414+
415+
if (!response.ok || !contentType || !contentType.includes("application/json")) {
414416
return;
415417
}
416418
console.log("data.json demo file found on the server root, saving it to Dataset 1");
@@ -455,15 +457,19 @@ class App extends React.Component {
455457
return { status: null, index };
456458
})
457459
);
458-
this.setState({
459-
uploadedDatasets: newUploadedDatasets.map((d) => d.status),
460-
});
461-
if (this.state.activeDatasetIndex === null) {
462-
const firstAvailableIndex = newUploadedDatasets.find((dataset) => dataset.status === "Uploaded")?.index;
463-
if (firstAvailableIndex !== undefined) {
464-
this.switchDataset(firstAvailableIndex);
460+
this.setState(
461+
{
462+
uploadedDatasets: newUploadedDatasets.map((d) => d.status),
463+
},
464+
() => {
465+
if (this.state.activeDatasetIndex === null) {
466+
const firstAvailableIndex = newUploadedDatasets.find((dataset) => dataset.status === "Uploaded")?.index;
467+
if (firstAvailableIndex !== undefined) {
468+
this.switchDataset(firstAvailableIndex);
469+
}
470+
}
465471
}
466-
}
472+
);
467473
};
468474

469475
handleLongPress = async (index) => {
@@ -723,43 +729,45 @@ class App extends React.Component {
723729
dialog.className = "cloud-logging-dialog";
724730

725731
document.body.appendChild(dialog);
732+
const dialogRootElement = document.createElement("div");
733+
dialog.appendChild(dialogRootElement);
734+
const dialogRoot = createRoot(dialogRootElement);
726735
dialog.showModal();
727736
return new Promise((resolve) => {
737+
const cleanupAndResolve = (result) => {
738+
dialogRoot.unmount();
739+
dialog.remove();
740+
resolve(result);
741+
};
728742
const handleError = (error) => {
729743
console.error("Cloud Logging Error:", error);
730744
toast.error(error.message || "Failed to fetch logs. Please try again.");
731-
dialog.remove();
732-
resolve(null);
745+
cleanupAndResolve(null);
733746
};
734747

735748
const handleFileUpload = (event) => {
736-
log("File upload selected from Cloud Logging dialog");
749+
console.log("File upload selected from Cloud Logging dialog");
737750
const file = event?.target?.files?.[0];
738751
if (file) {
739-
dialog.remove();
740-
resolve({ file });
752+
cleanupAndResolve({ file });
741753
}
742754
};
743755

744-
// Using direct DOM manipulation instead of ReactDOM.render to avoid React 17 issues
745-
const root = document.createElement("div");
746-
dialog.appendChild(root);
747-
748-
const cloudLogging = React.createElement(CloudLogging, {
756+
const cloudLoggingComponent = React.createElement(CloudLogging, {
749757
onLogsReceived: (logs) => {
750758
log(`Received ${logs.length} logs from Cloud Logging`);
751-
dialog.remove();
752759
if (logs.length === 0) {
753760
toast.warning("No logs found matching your criteria.");
754-
resolve(null);
761+
cleanupAndResolve(null);
755762
} else {
756763
resolve({ logs });
764+
cleanupAndResolve({ logs });
757765
}
758766
},
759767
onFileUpload: handleFileUpload,
760768
setError: handleError,
761769
});
762-
ReactDOM.render(cloudLogging, root);
770+
dialogRoot.render(cloudLoggingComponent);
763771
});
764772
}
765773

src/CloudLogging.js

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -173,9 +173,12 @@ const CloudLoggingForm = ({ onLogsReceived, onFileUpload }) => {
173173
if (error.message.includes("Failed to fetch") && retryCount < 2) {
174174
log(`Network error, retrying (attempt ${retryCount + 1})...`);
175175
return new Promise((resolve) => {
176-
setTimeout(() => {
177-
resolve(fetchLogBatch(token, filter, pageToken, retryCount + 1));
178-
}, 1000 * (retryCount + 1)); // Exponential backoff
176+
setTimeout(
177+
() => {
178+
resolve(fetchLogBatch(token, filter, pageToken, retryCount + 1));
179+
},
180+
1000 * (retryCount + 1) // Exponential backoff
181+
);
179182
});
180183
}
181184
throw error;

src/LogTable.js

Lines changed: 43 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
// src/LogTable.js
2-
32
import React, { useState } from "react";
43
import { useSortBy, useTable } from "react-table";
54
import { FixedSizeList as List } from "react-window";
@@ -59,15 +58,18 @@ function Table({ columns, data, onSelectionChange, listRef, selectedRow, centerO
5958
}
6059
};
6160

61+
const { key, ...restRowProps } = row.getRowProps({
62+
style: {
63+
...style,
64+
pointerEvents: "auto",
65+
color: hasError ? "darkred" : "inherit",
66+
},
67+
});
68+
6269
return (
6370
<div
64-
{...row.getRowProps({
65-
style: {
66-
...style,
67-
pointerEvents: "auto",
68-
color: hasError ? "darkred" : "inherit",
69-
},
70-
})}
71+
key={key}
72+
{...restRowProps}
7173
className={`logtable-row ${selectedRow === index ? "selected" : ""}`}
7274
onMouseDown={startPressTimer}
7375
onMouseUp={() => {
@@ -85,15 +87,19 @@ function Table({ columns, data, onSelectionChange, listRef, selectedRow, centerO
8587
}
8688
}}
8789
>
88-
{row.cells.map((cell) => (
89-
<div
90-
{...cell.getCellProps()}
91-
className={`logtable-cell ${cell.column.className || ""}`}
92-
style={{ width: cell.column.width }}
93-
>
94-
{cell.render("Cell")}
95-
</div>
96-
))}
90+
{row.cells.map((cell) => {
91+
const { key, ...restCellProps } = cell.getCellProps();
92+
return (
93+
<div
94+
key={key}
95+
{...restCellProps}
96+
className={`logtable-cell ${cell.column.className || ""}`}
97+
style={{ width: cell.column.width }}
98+
>
99+
{cell.render("Cell")}
100+
</div>
101+
);
102+
})}
97103
</div>
98104
);
99105
},
@@ -113,19 +119,26 @@ function Table({ columns, data, onSelectionChange, listRef, selectedRow, centerO
113119
<div>
114120
<div {...getTableProps()}>
115121
<div>
116-
{headerGroups.map((headerGroup) => (
117-
<div {...headerGroup.getHeaderGroupProps()} className="logtable-header-row">
118-
{headerGroup.headers.map((column) => (
119-
<div
120-
{...column.getHeaderProps()}
121-
className={`logtable-header-cell ${column.className || ""}`}
122-
style={{ width: column.width }}
123-
>
124-
{column.render("Header")}
125-
</div>
126-
))}
127-
</div>
128-
))}
122+
{headerGroups.map((headerGroup) => {
123+
const { key, ...restHeaderGroupProps } = headerGroup.getHeaderGroupProps();
124+
return (
125+
<div key={key} {...restHeaderGroupProps} className="logtable-header-row">
126+
{headerGroup.headers.map((column) => {
127+
const { key, ...restColumnProps } = column.getHeaderProps();
128+
return (
129+
<div
130+
key={key}
131+
{...restColumnProps}
132+
className={`logtable-header-cell ${column.className || ""}`}
133+
style={{ width: column.width }}
134+
>
135+
{column.render("Header")}
136+
</div>
137+
);
138+
})}
139+
</div>
140+
);
141+
})}
129142
</div>
130143
<div {...getTableBodyProps()}>
131144
<List

src/Map.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ function initializeMapObject(element) {
9191
});
9292
const jsMapView = new window.google.maps.journeySharing.JourneySharingMapView({
9393
element: element,
94-
locationProvider,
94+
locationProviders: [locationProvider],
9595
mapOptions: {
9696
mapId: mapId,
9797
mapTypeControl: true,
@@ -142,7 +142,11 @@ function MyMapComponent(props) {
142142
map.addListener(
143143
"center_changed",
144144
_.debounce(() => {
145-
setQueryStringValue("center", JSON.stringify(map.getCenter().toJSON()));
145+
const center = map.getCenter();
146+
if (center) {
147+
console.log("center_changed event fired, updating query string.");
148+
setQueryStringValue("center", JSON.stringify(center.toJSON()));
149+
}
146150
}, 100)
147151
);
148152

src/TimeSlider.js

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,12 @@
44
* Provides a time-based visualization of key events (vehicle status changes) as well
55
* as filtering control for the log viewer & map view.
66
*/
7-
import Slider from "rc-slider";
7+
const Slider = require("rc-slider").default;
88
import { useMemo, useRef, useEffect, useState } from "react";
99
import "rc-slider/assets/index.css";
1010
import _ from "lodash";
1111
import { log } from "./Utils";
1212

13-
const { createSliderWithTooltip } = Slider;
14-
const Range = createSliderWithTooltip(Slider.Range);
15-
1613
const style = { width: "100%" };
1714

1815
function TimeSlider(props) {
@@ -127,6 +124,7 @@ function TimeSlider(props) {
127124
container.appendChild(overlay);
128125

129126
const handleOverlayClick = (e) => {
127+
if (isDragging) return;
130128
const rect = container.getBoundingClientRect();
131129
const totalWidth = rect.width;
132130
const clickPosition = e.clientX - rect.left;
@@ -188,7 +186,8 @@ function TimeSlider(props) {
188186

189187
return (
190188
<div style={style} ref={sliderContainerRef}>
191-
<Range
189+
<Slider
190+
range
192191
min={minVal}
193192
max={maxVal}
194193
marks={marks}

0 commit comments

Comments
 (0)