From 5ba9d88f3d83813873cbcd4b5a2f563bcb03ff38 Mon Sep 17 00:00:00 2001 From: Olivier Terral Date: Tue, 10 Mar 2026 15:18:17 +0100 Subject: [PATCH 01/26] feat: use react 19 --- .eslintrc.js | 39 - jest.config.js | 2 +- package.json | 92 +- .../BaseLayerSwitcher/BaseLayerSwitcher.js | 5 +- .../BaseLayerSwitcher.test.js | 10 +- src/components/BasicMap/BasicMap.test.js | 3 +- .../CanvasSaveButton/CanvasSaveButton.js | 22 +- .../CanvasSaveButton/CanvasSaveButton.test.js | 8 +- .../CanvasSaveButton.test.js.snap | 2 +- src/components/Copyright/Copyright.js | 5 +- src/components/Copyright/Copyright.test.js | 3 +- .../__snapshots__/Copyright.test.js.snap | 2 +- .../FeatureExportButton.js | 2 - .../FeatureExportButton.test.js | 3 +- .../FeatureExportButton.test.js.snap | 2 +- src/components/FitExtent/FitExtent.js | 1 - src/components/FitExtent/FitExtent.test.js | 64 +- .../__snapshots__/FitExtent.test.js.snap | 4 +- src/components/Geolocation/Geolocation.js | 1 - .../Geolocation/Geolocation.test.js | 5 +- .../__snapshots__/Geolocation.test.js.snap | 2 +- src/components/LayerTree/LayerTree.js | 41 +- src/components/LayerTree/LayerTree.test.js | 4 +- .../__snapshots__/LayerTree.test.js.snap | 2 +- src/components/MousePosition/MousePosition.js | 3 +- .../MousePosition/MousePosition.test.js | 4 +- .../__snapshots__/MousePosition.test.js.snap | 2 +- src/components/NorthArrow/NorthArrow.js | 1 - src/components/NorthArrow/NorthArrow.test.js | 16 +- .../__snapshots__/NorthArrow.test.js.snap | 27 +- .../__snapshots__/Overlay.test.js.snap | 2 +- src/components/Permalink/Permalink.js | 3 - src/components/Permalink/Permalink.test.js | 3 +- src/components/Popup/Popup.js | 3 +- src/components/Popup/Popup.test.js | 7 +- .../Popup/__snapshots__/Popup.test.js.snap | 2 +- src/components/ResizeHandler/ResizeHandler.js | 4 +- .../ResizeHandler/ResizeHandler.test.js | 4 +- src/components/RouteSchedule/RouteSchedule.js | 4 +- .../RouteSchedule/RouteSchedule.test.js | 3 +- src/components/ScaleLine/ScaleLine.js | 1 - src/components/ScaleLine/ScaleLine.test.js | 3 +- .../__snapshots__/ScaleLine.test.js.snap | 2 +- src/components/StopsFinder/StopsFinder.js | 23 +- .../StopsFinder/StopsFinderOption.js | 13 +- .../__snapshots__/StopsFinder.test.js.snap | 8 +- src/components/Zoom/Zoom.js | 1 - src/components/Zoom/Zoom.test.js | 40 +- .../Zoom/__snapshots__/Zoom.test.js.snap | 2 +- src/setupTests.js | 12 + src/styleguidist/ComponentsList.js | 1 - src/styleguidist/StyleGuide.js | 14 +- src/utils/KML.js | 6 +- styleguide.config.js | 10 + yarn.lock | 5129 ++++++++++------- 55 files changed, 3308 insertions(+), 2369 deletions(-) delete mode 100644 .eslintrc.js diff --git a/.eslintrc.js b/.eslintrc.js deleted file mode 100644 index f4b3f1cf..00000000 --- a/.eslintrc.js +++ /dev/null @@ -1,39 +0,0 @@ -module.exports = { - env: { - node: true, - browser: true, - es6: true, - jest: true, - }, - extends: [ - "airbnb", - "airbnb/hooks", - "plugin:perfectionist/recommended-alphabetical-legacy", - "prettier", - ], - parserOptions: { - ecmaVersion: "latest", - }, - plugins: ["perfectionist", "prettier"], - rules: { - "arrow-body-style": "off", - "import/no-extraneous-dependencies": [ - "error", - { - devDependencies: true, - }, - ], - "react/sort-comp": "off", - "react/jsx-filename-extension": [ - 1, - { - extensions: [".js", ".jsx"], - }, - ], - "no-restricted-exports": "Off", - "react/forbid-prop-types": "Off", - "prettier/prettier": "error", - "jsx-a11y/no-access-key": "Off", - "react/require-default-props": "Off", - }, -}; diff --git a/jest.config.js b/jest.config.js index ac591a81..f79dd964 100644 --- a/jest.config.js +++ b/jest.config.js @@ -10,8 +10,8 @@ module.exports = { testMatch: ["/src/**/?(*.)+(spec|test).[jt]s?(x)"], testPathIgnorePatterns: ["/(build|coverage|public|doc|packages)"], transform: { - ".+\\.js$": "babel-jest", ".+\\.svg$": "jest-transformer-svg", + "^.+\\.(js|jsx|ts|tsx)$": "babel-jest", }, transformIgnorePatterns: [], }; diff --git a/package.json b/package.json index 28dcac6f..cb148f73 100644 --- a/package.json +++ b/package.json @@ -6,94 +6,84 @@ "dependencies": { "@emotion/react": "^11.14.0", "@emotion/styled": "^11.14.1", - "@geops/geops-ui": "0.3.6-beta.0", - "@mui/icons-material": "^7.3.1", - "@mui/material": "^7.3.1", + "@geops/geops-ui": "1.0.1-beta.3", + "@mui/icons-material": "^7.3.9", + "@mui/material": "^7.3.9", "re-resizable": "6.11.2", - "react-icons": "5.5.0", - "react-is": "18.3.1", + "react-icons": "5.6.0", "resize-observer-polyfill": "1.5.1" }, "peerDependencies": { "maplibre-gl": "^4", "mobility-toolbox-js": "^3", "ol": "^10", - "react": "^18", - "react-dom": "^18" + "react": "^19", + "react-dom": "^19" }, "devDependencies": { - "@babel/preset-env": "7.28.0", - "@babel/preset-react": "7.27.1", - "@commitlint/cli": "19.8.1", - "@commitlint/config-conventional": "19.8.1", + "@babel/preset-env": "7.29.0", + "@babel/preset-react": "7.28.5", + "@commitlint/cli": "20.4.3", + "@commitlint/config-conventional": "20.4.3", + "@geops/eslint-config-react": "^1.6.0", "@svgr/plugin-jsx": "^8.1.0", "@svgr/webpack": "8.1.0", "@testing-library/dom": "^10.4.1", - "@testing-library/jest-dom": "6.6.4", - "@testing-library/react": "16.3.0", + "@testing-library/jest-dom": "6.9.1", + "@testing-library/react": "16.3.2", "@testing-library/user-event": "14.6.1", - "babel-jest": "30.0.5", - "babel-loader": "10.0.0", - "canvas": "3.1.2", - "css-loader": "7.1.2", + "babel-jest": "30.3.0", + "babel-loader": "10.1.1", + "canvas": "3.2.1", + "css-loader": "7.1.4", "enzyme": "3.11.0", - "esbuild": "^0.25.8", - "esbuild-loader": "^4.3.0", - "eslint": "8", - "eslint-config-airbnb": "19.0.4", - "eslint-config-prettier": "9.1.0", - "eslint-plugin-import": "2.31.0", - "eslint-plugin-jsx-a11y": "^6.10.2", - "eslint-plugin-perfectionist": "^4.2.0", - "eslint-plugin-prettier": "5.2.1", - "eslint-plugin-react": "7.37.2", - "eslint-plugin-react-hooks": "5.1.0", + "esbuild": "^0.27.3", + "esbuild-loader": "^4.4.2", + "eslint": "9", "file-loader": "6.2.0", "fixpack": "4.0.0", "generact": "0.4.0", "husky": "9.1.7", "identity-obj-proxy": "^3.0.0", "is-ci": "4.1.0", - "jest": "30.0.5", + "jest": "30.3.0", "jest-canvas-mock": "2.5.2", "jest-date-mock": "1.0.10", - "jest-environment-jsdom": "^30.0.5", + "jest-environment-jsdom": "^30.3.0", "jest-fetch-mock": "3.0.3", "jest-serializer-html": "7.1.0", "jest-transform-file": "1.1.1", "jest-transformer-svg": "^2.1.0", "jsts": "2.12.1", - "lint-staged": "16.1.5", - "maplibre-gl": "5.6.2", - "mobility-toolbox-js": "3.4.4", - "ol": "10.6.1", - "postcss": "^8.5.6", - "prettier": "3.6.2", - "proj4": "2.19.10", + "lint-staged": "16.3.3", + "maplibre-gl": "5.19.0", + "mobility-toolbox-js": "3.6.11", + "ol": "10.8.0", + "postcss": "^8.5.8", + "prettier": "3.8.1", + "proj4": "2.20.4", "prop-types": "15.8.1", - "react": "18.3.1", - "react-dom": "18.3.1", + "react": "19.2.4", + "react-dom": "19.2.4", "react-styleguidist": "13.1.4", "react-svg-loader": "3.0.3", - "sass": "1.90.0", - "sass-loader": "16.0.5", - "sass-migrator": "^2.4.2", + "sass": "1.97.3", + "sass-loader": "16.0.7", + "sass-migrator": "^2.5.7", "standard-version": "9.5.0", "stream-array": "1.1.2", "style-loader": "4.0.0", - "stylelint": "16.23.1", - "stylelint-config-recommended-scss": "16.0.0", - "stylelint-config-standard": "39.0.0", - "stylelint-scss": "6.12.1", - "terser-webpack-plugin": "^5.3.14", + "stylelint": "17.4.0", + "stylelint-config-recommended-scss": "17.0.0", + "stylelint-config-standard": "40.0.0", + "stylelint-scss": "7.0.0", + "terser-webpack-plugin": "^5.3.17", + "typescript": "5", "url-loader": "4.1.1", "vinyl-fs": "4.0.2", - "webpack": "^5.101.0", + "webpack": "^5.105.4", "xml-beautifier": "0.5.0" }, - "resolutions": { - "react-is": "18.3.1" - }, "scripts": { "build": "yarn esbuild && find build -type f -name '*.test.*' -delete && rm -rf build/styleguidist && cp package.json README.md LICENSE build && cp -rf src/images build && cp -rf src/themes build", "coverage": "yarn test --coverage --coverageDirectory=coverage", diff --git a/src/components/BaseLayerSwitcher/BaseLayerSwitcher.js b/src/components/BaseLayerSwitcher/BaseLayerSwitcher.js index 6e7f9d06..e648dc42 100644 --- a/src/components/BaseLayerSwitcher/BaseLayerSwitcher.js +++ b/src/components/BaseLayerSwitcher/BaseLayerSwitcher.js @@ -1,7 +1,6 @@ import Layer from "ol/layer/Layer"; import { unByKey } from "ol/Observable"; import PropTypes from "prop-types"; -/* eslint-disable jsx-a11y/interactive-supports-focus */ import React, { useEffect, useState } from "react"; import { FaChevronLeft } from "react-icons/fa"; @@ -190,7 +189,7 @@ function BaseLayerSwitcher({ } return; } - // eslint-disable-next-line consistent-return + return setSwitcherOpen(true) && setIsClosed(false); }; @@ -206,7 +205,6 @@ function BaseLayerSwitcher({ if (layer.setVisible) { layer.setVisible(true); } else { - // eslint-disable-next-line no-param-reassign layer.visible = true; } layers @@ -217,7 +215,6 @@ function BaseLayerSwitcher({ if (l.setVisible) { l.setVisible(false); } else { - // eslint-disable-next-line no-param-reassign l.visible = false; } }); diff --git a/src/components/BaseLayerSwitcher/BaseLayerSwitcher.test.js b/src/components/BaseLayerSwitcher/BaseLayerSwitcher.test.js index e3823af6..717ecaf7 100644 --- a/src/components/BaseLayerSwitcher/BaseLayerSwitcher.test.js +++ b/src/components/BaseLayerSwitcher/BaseLayerSwitcher.test.js @@ -42,12 +42,12 @@ describe("BaseLayerSwitcher", () => { expect(layers[0].getVisible()).toBe(true); }); - test("removes open class and switches layer on click", async () => { + test("removes open class and switches layer on click", () => { const { container } = render( , ); - await fireEvent.click(container.querySelector(".rs-opener")); - await fireEvent.click( + fireEvent.click(container.querySelector(".rs-opener")); + fireEvent.click( container.querySelectorAll(".rs-base-layer-switcher-button")[3], ); expect(layers[2].getVisible()).toBe(true); @@ -56,12 +56,12 @@ describe("BaseLayerSwitcher", () => { ); }); - test("toggles base map instead of opening when only two base layers", async () => { + test("toggles base map instead of opening when only two base layers", () => { const { container } = render( , ); expect(layers[0].getVisible()).toBe(true); - await fireEvent.click(container.querySelector(".rs-opener")); + fireEvent.click(container.querySelector(".rs-opener")); expect(layers[1].getVisible()).toBe(true); expect(!!container.querySelector(".rs-base-layer-switcher rs-open")).toBe( false, diff --git a/src/components/BasicMap/BasicMap.test.js b/src/components/BasicMap/BasicMap.test.js index 31f7c458..b38e49b1 100644 --- a/src/components/BasicMap/BasicMap.test.js +++ b/src/components/BasicMap/BasicMap.test.js @@ -1,5 +1,4 @@ import { render } from "@testing-library/react"; -import "jest-canvas-mock"; import { Layer as OldMbtLayer } from "mobility-toolbox-js/ol"; import OLLayer from "ol/layer/Vector"; import OLMap from "ol/Map"; @@ -11,6 +10,8 @@ import React from "react"; import BasicMap from "./BasicMap"; +import "jest-canvas-mock"; + proj4.defs( "EPSG:21781", "+proj=somerc +lat_0=46.95240555555556 " + diff --git a/src/components/CanvasSaveButton/CanvasSaveButton.js b/src/components/CanvasSaveButton/CanvasSaveButton.js index 6e54037a..ab2406e4 100644 --- a/src/components/CanvasSaveButton/CanvasSaveButton.js +++ b/src/components/CanvasSaveButton/CanvasSaveButton.js @@ -172,7 +172,6 @@ const decreaseFontSize = (destContext, maxWidth, copyright, scale) => { sizeMatch = destContext.font.match(/[0-9]+(?:\.[0-9]+)?(px)/i); fontSize = parseInt(sizeMatch[0].replace(sizeMatch[1], ""), 10); - // eslint-disable-next-line no-param-reassign destContext.font = destContext.font.replace(fontSize, fontSize - 1); multilineCopyright = false; @@ -188,7 +187,6 @@ const decreaseFontSize = (destContext, maxWidth, copyright, scale) => { return destContext.font; }; -// eslint-disable-next-line class-methods-use-this const drawTextBackground = ( destContext, x, @@ -261,7 +259,11 @@ const drawCopyright = ( const secondLine = copyright.replace(firstLine, "").trim(); // At this point we the number of lines to display. - const lines = [firstLine, secondLine].filter((l) => !!l).reverse(); + const lines = [firstLine, secondLine] + .filter((l) => { + return !!l; + }) + .reverse(); // We draw from bottom to top because textBaseline is 'bottom let lineX = margin; @@ -416,7 +418,6 @@ const createCanvasImage = ( for (let i = 0; i < canvases.length; i += 1) { const canvas = canvases[i]; if (!canvas.width || !canvas.height) { - // eslint-disable-next-line no-continue continue; } const clip = calculatePixelsToExport( @@ -455,7 +456,7 @@ const createCanvasImage = ( // Custom info let logoPromise = Promise.resolve(); - if (destContext && extraData && extraData.logo) { + if (destContext && extraData?.logo) { logoPromise = drawElement( extraData.logo, destCanvas, @@ -468,7 +469,7 @@ const createCanvasImage = ( logoPromise.then((logoSize = [0, 0]) => { // North arrow let arrowPromise = Promise.resolve(); - if (destContext && extraData && extraData.northArrow) { + if (destContext && extraData?.northArrow) { arrowPromise = drawElement( { src: extraData.northArrow.circled @@ -487,12 +488,7 @@ const createCanvasImage = ( // Copyright arrowPromise.then((arrowSize = [0, 0]) => { const widestElement = Math.max(logoSize[0], arrowSize[0]); - if ( - destContext && - extraData && - extraData.copyright && - extraData.copyright.text - ) { + if (destContext && extraData?.copyright?.text) { const maxWidth = extraData.copyright.maxWidth || (widestElement @@ -509,7 +505,7 @@ const createCanvasImage = ( ); } let qrCodePromise = Promise.resolve(); - if (destContext && extraData && extraData.qrCode) { + if (destContext && extraData?.qrCode) { qrCodePromise = drawElement( extraData.qrCode, destCanvas, diff --git a/src/components/CanvasSaveButton/CanvasSaveButton.test.js b/src/components/CanvasSaveButton/CanvasSaveButton.test.js index 0dc3ad7c..ea77d0bd 100644 --- a/src/components/CanvasSaveButton/CanvasSaveButton.test.js +++ b/src/components/CanvasSaveButton/CanvasSaveButton.test.js @@ -1,6 +1,4 @@ -/* eslint-disable react/button-has-type */ import { fireEvent, render } from "@testing-library/react"; -import "jest-canvas-mock"; import Map from "ol/Map"; import RenderEvent from "ol/render/Event"; import View from "ol/View"; @@ -9,6 +7,8 @@ import { TiImage } from "react-icons/ti"; import CanvasSaveButton from "./CanvasSaveButton"; +import "jest-canvas-mock"; + describe("CanvasSaveButton", () => { let olMap; const conf = { @@ -119,13 +119,13 @@ describe("CanvasSaveButton", () => { .spyOn(olMap.getTargetElement(), "getElementsByTagName") .mockReturnValue([canvas]); - await fireEvent.click(wrapper.container.querySelector(".ta-example")); + fireEvent.click(wrapper.container.querySelector(".ta-example")); await olMap.dispatchEvent( new RenderEvent("rendercomplete", undefined, undefined, { canvas, }), ); - await window.setTimeout(() => { + window.setTimeout(() => { expect(saveStart).toHaveBeenCalledTimes(1); expect(saveEnd).toHaveBeenCalledTimes(1); expect(link.href).toBe("http://localhost/fooblob"); diff --git a/src/components/CanvasSaveButton/__snapshots__/CanvasSaveButton.test.js.snap b/src/components/CanvasSaveButton/__snapshots__/CanvasSaveButton.test.js.snap index 99bb1285..57e70965 100644 --- a/src/components/CanvasSaveButton/__snapshots__/CanvasSaveButton.test.js.snap +++ b/src/components/CanvasSaveButton/__snapshots__/CanvasSaveButton.test.js.snap @@ -1,4 +1,4 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing exports[`CanvasSaveButton should match snapshot with a different attributes 1`] = `