diff --git a/webapp/.eslintrc.cjs b/webapp/.eslintrc.cjs index bd9d45c683..74a900fee9 100644 --- a/webapp/.eslintrc.cjs +++ b/webapp/.eslintrc.cjs @@ -11,12 +11,14 @@ module.exports = { "plugin:react/recommended", "plugin:react/jsx-runtime", "plugin:react-hooks/recommended", + "plugin:jsdoc/recommended-typescript", "plugin:prettier/recommended", ], plugins: ["react-refresh"], ignorePatterns: ["dist", ".eslintrc.cjs"], parser: "@typescript-eslint/parser", parserOptions: { + // `ecmaVersion` is automatically sets by `esXXXX` in `env` sourceType: "module", project: ["./tsconfig.json", "./tsconfig.node.json"], tsconfigRootDir: __dirname, @@ -41,6 +43,10 @@ module.exports = { ], }, ], + curly: "error", + "jsdoc/require-hyphen-before-param-description": "warn", + "jsdoc/require-jsdoc": "off", + "jsdoc/tag-lines": ["warn", "any", { "startLines": 1 }], // Expected 1 line after block description "no-param-reassign": [ "error", { @@ -65,6 +71,8 @@ module.exports = { "warn", { allowConstantExport: true }, ], + "react/hook-use-state": "error", "react/prop-types": "off", + "react/self-closing-comp": "error", }, }; diff --git a/webapp/package-lock.json b/webapp/package-lock.json index 2c81f56aa3..3baefa00f7 100644 --- a/webapp/package-lock.json +++ b/webapp/package-lock.json @@ -92,6 +92,7 @@ "@vitejs/plugin-react-swc": "3.5.0", "eslint": "8.55.0", "eslint-config-prettier": "9.0.0", + "eslint-plugin-jsdoc": "48.2.0", "eslint-plugin-prettier": "5.0.0", "eslint-plugin-react": "7.33.2", "eslint-plugin-react-hooks": "4.6.0", @@ -479,6 +480,20 @@ "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.3.1.tgz", "integrity": "sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww==" }, + "node_modules/@es-joy/jsdoccomment": { + "version": "0.42.0", + "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.42.0.tgz", + "integrity": "sha512-R1w57YlVA6+YE01wch3GPYn6bCsrOV3YW/5oGGE2tmX6JcL9Nr+b5IikrjMPF+v9CV3ay+obImEdsDhovhJrzw==", + "dev": true, + "dependencies": { + "comment-parser": "1.4.1", + "esquery": "^1.5.0", + "jsdoc-type-pratt-parser": "~4.0.0" + }, + "engines": { + "node": ">=16" + } + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.19.12", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.19.12.tgz", @@ -3951,6 +3966,15 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/are-docs-informative": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/are-docs-informative/-/are-docs-informative-0.0.2.tgz", + "integrity": "sha512-ixiS0nLNNG5jNQzgZJNoUpBKdo9yTYZMGJ+QgT2jmjR7G7+QHRCc4v6LQ3NgE7EBJq+o0ams3waJwkrlBom8Ig==", + "dev": true, + "engines": { + "node": ">=14" + } + }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -4287,6 +4311,18 @@ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" }, + "node_modules/builtin-modules": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", + "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==", + "dev": true, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/call-bind": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz", @@ -4501,6 +4537,15 @@ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" }, + "node_modules/comment-parser": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-1.4.1.tgz", + "integrity": "sha512-buhp5kePrmda3vhc5B9t7pUQXAb2Tnd0qgpkIhPhkHXxJpiPJ11H0ZEU0oBpJ2QztSbzG/ZxMj/CHsYJqRHmyg==", + "dev": true, + "engines": { + "node": ">= 12.0.0" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -5578,6 +5623,29 @@ "eslint": ">=7.0.0" } }, + "node_modules/eslint-plugin-jsdoc": { + "version": "48.2.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-48.2.0.tgz", + "integrity": "sha512-O2B1XLBJnUCRkggFzUQ+PBYJDit8iAgXdlu8ucolqGrbmOWPvttZQZX8d1sC0MbqDMSLs8SHSQxaNPRY1RQREg==", + "dev": true, + "dependencies": { + "@es-joy/jsdoccomment": "~0.42.0", + "are-docs-informative": "^0.0.2", + "comment-parser": "1.4.1", + "debug": "^4.3.4", + "escape-string-regexp": "^4.0.0", + "esquery": "^1.5.0", + "is-builtin-module": "^3.2.1", + "semver": "^7.6.0", + "spdx-expression-parse": "^4.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0 || ^9.0.0" + } + }, "node_modules/eslint-plugin-prettier": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.0.0.tgz", @@ -7180,6 +7248,21 @@ "resolved": "https://registry.npmjs.org/is-browser/-/is-browser-2.1.0.tgz", "integrity": "sha512-F5rTJxDQ2sW81fcfOR1GnCXT6sVJC104fCyfj+mjpwNEwaPYSn5fte5jiHmBg3DHsIoL/l8Kvw5VN5SsTRcRFQ==" }, + "node_modules/is-builtin-module": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.1.tgz", + "integrity": "sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==", + "dev": true, + "dependencies": { + "builtin-modules": "^3.3.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-callable": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", @@ -7609,6 +7692,15 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/jsdoc-type-pratt-parser": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-4.0.0.tgz", + "integrity": "sha512-YtOli5Cmzy3q4dP26GraSOeAhqecewG04hoO8DY56CH4KJ9Fvv5qKWUCCo3HZob7esJQHCv6/+bnTy72xZZaVQ==", + "dev": true, + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/json-buffer": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", @@ -9999,9 +10091,9 @@ } }, "node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", "dependencies": { "lru-cache": "^6.0.0" }, @@ -10211,6 +10303,28 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/spdx-exceptions": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", + "dev": true + }, + "node_modules/spdx-expression-parse": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-4.0.0.tgz", + "integrity": "sha512-Clya5JIij/7C6bRR22+tnGXbc4VKlibKSVj2iHvVeX5iMW7s1SIQlqu699JkODJJIhh/pUu8L0/VLh8xflD+LQ==", + "dev": true, + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.17", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.17.tgz", + "integrity": "sha512-sh8PWc/ftMqAAdFiBu6Fy6JUOYjqDJBJvIhpfDMyHrr0Rbp5liZqd4TjtQ/RgfLjKFZb+LMx5hpml5qOWy0qvg==", + "dev": true + }, "node_modules/split.js": { "version": "1.6.5", "resolved": "https://registry.npmjs.org/split.js/-/split.js-1.6.5.tgz", diff --git a/webapp/package.json b/webapp/package.json index e21bd5dd7e..4a4c3e5645 100644 --- a/webapp/package.json +++ b/webapp/package.json @@ -95,6 +95,7 @@ "@vitejs/plugin-react-swc": "3.5.0", "eslint": "8.55.0", "eslint-config-prettier": "9.0.0", + "eslint-plugin-jsdoc": "48.2.0", "eslint-plugin-prettier": "5.0.0", "eslint-plugin-react": "7.33.2", "eslint-plugin-react-hooks": "4.6.0", diff --git a/webapp/src/components/App/Data/DatasetCreationDialog.tsx b/webapp/src/components/App/Data/DatasetCreationDialog.tsx index 41304025a2..c10b1eaaf4 100644 --- a/webapp/src/components/App/Data/DatasetCreationDialog.tsx +++ b/webapp/src/components/App/Data/DatasetCreationDialog.tsx @@ -50,9 +50,9 @@ function DatasetCreationDialog(props: PropTypes) { const [name, setName] = useState(""); const [isJson, setIsJson] = useState(false); const [uploadProgress, setUploadProgress] = useState(0); - const [currentFile, setFile] = useState(); + const [currentFile, setCurrentFile] = useState(); const [importing, setImporting] = useState(false); - const [publicStatus, setPublic] = useState(false); + const [publicStatus, setPublicStatus] = useState(false); const onSave = async () => { let closeModal = true; @@ -93,7 +93,7 @@ function DatasetCreationDialog(props: PropTypes) { const onUpload = (e: ChangeEvent) => { const { target } = e; if (target && target.files && target.files.length === 1) { - setFile(target.files[0]); + setCurrentFile(target.files[0]); } }; @@ -116,7 +116,7 @@ function DatasetCreationDialog(props: PropTypes) { if (data) { setSelectedGroupList(data.groups); - setPublic(data.public); + setPublicStatus(data.public); setName(data.name); } } catch (e) { @@ -249,7 +249,7 @@ function DatasetCreationDialog(props: PropTypes) { {t("global.public")} setPublic(!publicStatus)} + onChange={() => setPublicStatus(!publicStatus)} inputProps={{ "aria-label": "primary checkbox" }} /> diff --git a/webapp/src/components/App/Data/MatrixDialog.tsx b/webapp/src/components/App/Data/MatrixDialog.tsx index ddd7a59b85..df2b14ab99 100644 --- a/webapp/src/components/App/Data/MatrixDialog.tsx +++ b/webapp/src/components/App/Data/MatrixDialog.tsx @@ -17,7 +17,7 @@ function MatrixDialog(props: PropTypes) { const [t] = useTranslation(); const enqueueErrorSnackbar = useEnqueueErrorSnackbar(); const [loading, setLoading] = useState(false); - const [matrix, setCurrentMatrix] = useState({ + const [matrix, setMatrix] = useState({ index: [], columns: [], data: [], @@ -34,7 +34,7 @@ function MatrixDialog(props: PropTypes) { columns: matrix ? res.columns : [], data: matrix ? res.data : [], }; - setCurrentMatrix(matrixContent); + setMatrix(matrixContent); } } catch (error) { enqueueErrorSnackbar(t("data.error.matrix"), error as AxiosError); @@ -44,7 +44,7 @@ function MatrixDialog(props: PropTypes) { }; init(); return () => { - setCurrentMatrix({ index: [], columns: [], data: [] }); + setMatrix({ index: [], columns: [], data: [] }); }; // eslint-disable-next-line react-hooks/exhaustive-deps }, [enqueueErrorSnackbar, matrixInfo, t]); diff --git a/webapp/src/components/App/Singlestudy/explore/Debug/Data/Json.tsx b/webapp/src/components/App/Singlestudy/explore/Debug/Data/Json.tsx index 334fa84638..3923ce319a 100644 --- a/webapp/src/components/App/Singlestudy/explore/Debug/Data/Json.tsx +++ b/webapp/src/components/App/Singlestudy/explore/Debug/Data/Json.tsx @@ -22,7 +22,7 @@ function Json({ path, studyId }: Props) { const { enqueueSnackbar } = useSnackbar(); const enqueueErrorSnackbar = useEnqueueErrorSnackbar(); const [jsonData, setJsonData] = useState(null); - const [isSaveAllowed, setSaveAllowed] = useState(false); + const [isSaveAllowed, setIsSaveAllowed] = useState(false); const res = usePromiseWithSnackbarError( () => getStudyData(studyId, path, -1), @@ -34,7 +34,7 @@ function Json({ path, studyId }: Props) { // Reset save button when path changes useUpdateEffect(() => { - setSaveAllowed(false); + setIsSaveAllowed(false); }, [studyId, path]); //////////////////////////////////////////////////////////////// @@ -48,7 +48,7 @@ function Json({ path, studyId }: Props) { enqueueSnackbar(t("studies.success.saveData"), { variant: "success", }); - setSaveAllowed(false); + setIsSaveAllowed(false); } catch (e) { enqueueErrorSnackbar(t("studies.error.saveData"), e as AxiosError); } @@ -57,7 +57,7 @@ function Json({ path, studyId }: Props) { const handleJsonChange = (newJson: string) => { setJsonData(newJson); - setSaveAllowed(true); + setIsSaveAllowed(true); }; //////////////////////////////////////////////////////////////// diff --git a/webapp/src/components/App/Singlestudy/explore/Debug/utils.ts b/webapp/src/components/App/Singlestudy/explore/Debug/utils.ts index e97f0e00a7..a6e3798806 100644 --- a/webapp/src/components/App/Singlestudy/explore/Debug/utils.ts +++ b/webapp/src/components/App/Singlestudy/explore/Debug/utils.ts @@ -22,9 +22,7 @@ export type TreeData = Record | string; // Utils //////////////////////////////////////////////////////////////// -/** - * Maps file types and folder to their corresponding icon components. - */ +//Maps file types and folder to their corresponding icon components. const iconByFileType: Record = { matrix: DatasetIcon, json: DataObjectIcon, @@ -34,8 +32,9 @@ const iconByFileType: Record = { /** * Gets the icon component for a given file type or folder. - * @param {FileType | "folder"} type - The type of the file or "folder". - * @returns {SvgIconComponent} The corresponding icon component. + * + * @param type - The type of the file or "folder". + * @returns The corresponding icon component. */ export const getFileIcon = (type: FileType | "folder"): SvgIconComponent => { return iconByFileType[type] || TextSnippetIcon; @@ -43,8 +42,9 @@ export const getFileIcon = (type: FileType | "folder"): SvgIconComponent => { /** * Determines the file type based on the tree data. - * @param {TreeData} treeData - The data of the tree item. - * @returns {FileType | "folder"} The determined file type or "folder". + * + * @param treeData - The data of the tree item. + * @returns The determined file type or "folder". */ export const determineFileType = (treeData: TreeData): FileType | "folder" => { if (typeof treeData === "string") { @@ -63,8 +63,9 @@ export const determineFileType = (treeData: TreeData): FileType | "folder" => { /** * Filters out specific keys from the tree data. - * @param {TreeData} data - The original tree data. - * @returns {TreeData} The filtered tree data. + * + * @param data - The original tree data. + * @returns The filtered tree data. */ export const filterTreeData = (data: TreeData): TreeData => { const excludedKeys = new Set(["Desktop", "study", "logs"]); diff --git a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/utils.ts b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/utils.ts index ed8457afe4..7b4878aa73 100644 --- a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/utils.ts +++ b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/Hydro/utils.ts @@ -210,7 +210,8 @@ export const MATRICES: Matrices = { /** * Generates an array of column names from 0 to 100, optionally with a suffix. - * @param columnSuffix The suffix to append to the column names. + * + * @param columnSuffix - The suffix to append to the column names. * @returns An array of strings representing column names from 0 to 100. */ function generateColumns(columnSuffix = ""): string[] { diff --git a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/common/utils.ts b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/common/utils.ts index 8528245c84..9231804f53 100644 --- a/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/common/utils.ts +++ b/webapp/src/components/App/Singlestudy/explore/Modelization/Areas/common/utils.ts @@ -20,17 +20,21 @@ export const saveField = R.curry( /** * Custom aggregation function summing the values of each row, - * to display enabled and installed capacity in the same cell. - * @param colHeader - the column header - * @param rows - the column rows to aggregate - * @returns a string with the sum of enabled and installed capacity. - * @example "100/200" - * @see https://www.material-react-table.com/docs/guides/aggregation-and-grouping#custom-aggregation-functions + * to display enabled and installed capacity in the same cell. This function is + * designed for use with Material React Table's custom aggregation feature, allowing + * the combination of enabled and installed capacities into a single cell. + * + * @returns A string representing the sum of enabled and installed capacity in the format "enabled/installed". + * @example + * Assuming an aggregation of rows where enabled capacities sum to 100 and installed capacities sum to 200 + * "100/200" + * + * @see https://www.material-react-table.com/docs/guides/aggregation-and-grouping#custom-aggregation-functions for more information on custom aggregation functions in Material React Table. */ export const capacityAggregationFn = < T extends ThermalClusterWithCapacity | RenewableClusterWithCapacity, >(): MRT_AggregationFn => { - return (colHeader, rows) => { + return (_colHeader, rows) => { const { enabledCapacitySum, installedCapacitySum } = rows.reduce( (acc, row) => { acc.enabledCapacitySum += row.original.enabledCapacity ?? 0; diff --git a/webapp/src/components/App/Singlestudy/explore/Modelization/Map/utils.ts b/webapp/src/components/App/Singlestudy/explore/Modelization/Map/utils.ts index 742ebe152a..1117d461a5 100644 --- a/webapp/src/components/App/Singlestudy/explore/Modelization/Map/utils.ts +++ b/webapp/src/components/App/Singlestudy/explore/Modelization/Map/utils.ts @@ -83,7 +83,15 @@ export const getTextColor = (bgColor: RGB): string => { //////////////////////////////////////////////////////////////// /** - * Sets the graph nodes from the nodes data + * Custom hook to compute and return nodes with adjusted positions based on the current layer and view settings. + * It adjusts node positions to ensure they are correctly positioned in the graph based on the current zoom level and layer. + * Additionally, it calculates the color for each node, supporting layer-specific color adjustments. + * + * @param nodes - Array of nodes to render. + * @param width - Width of the rendering area. + * @param height - Height of the rendering area. + * @param currentLayerId - The ID of the current layer, used to adjust node positions and colors. + * @returns Array of nodes with updated positions and colors for rendering. */ export function useRenderNodes( nodes: StudyMapNode[], diff --git a/webapp/src/components/App/Singlestudy/explore/Results/ResultDetails/index.tsx b/webapp/src/components/App/Singlestudy/explore/Results/ResultDetails/index.tsx index e1c3748b73..425245f9db 100644 --- a/webapp/src/components/App/Singlestudy/explore/Results/ResultDetails/index.tsx +++ b/webapp/src/components/App/Singlestudy/explore/Results/ResultDetails/index.tsx @@ -62,7 +62,7 @@ function ResultDetails() { const { data: output } = outputRes; const [dataType, setDataType] = useState(DataType.General); - const [timestep, setTimeStep] = useState(Timestep.Hourly); + const [timestep, setTimestep] = useState(Timestep.Hourly); const [year, setYear] = useState(-1); const [itemType, setItemType] = useState(OutputItemType.Areas); const [selectedItemId, setSelectedItemId] = useState(""); @@ -151,7 +151,9 @@ function ResultDetails() { // !NOTE: Workaround to display the date in the correct format, to be replaced by a proper solution. const dateTimeFromIndex = useMemo(() => { - if (!matrixRes.data) return []; + if (!matrixRes.data) { + return []; + } // Annual format has a static string if (timestep === Timestep.Annual) { @@ -359,7 +361,7 @@ function ResultDetails() { size="small" variant="outlined" onChange={(event) => { - setTimeStep(event?.target.value as Timestep); + setTimestep(event?.target.value as Timestep); }} /> ), diff --git a/webapp/src/components/App/Singlestudy/explore/TableModeList/utils.ts b/webapp/src/components/App/Singlestudy/explore/TableModeList/utils.ts index bcd307a954..0d8060c307 100644 --- a/webapp/src/components/App/Singlestudy/explore/TableModeList/utils.ts +++ b/webapp/src/components/App/Singlestudy/explore/TableModeList/utils.ts @@ -21,7 +21,13 @@ export interface TableTemplate { //////////////////////////////////////////////////////////////// /** - * Allows to check columns validity for specified type. + * Allows to check columns validity for specified type. Creates a table template with unique ID, name, type, and columns configuration. + * This function is intended to define the structure and type of data that a table can hold. + * + * @param name - The name of the table template. + * @param type - The type of the table, determining the allowed columns and their configuration based on the table mode type. + * @param columns - The configuration of columns specific to the table mode type. + * @returns A table template object including a unique ID, name, type, and columns configuration. */ export function createTableTemplate( name: string, diff --git a/webapp/src/components/App/Singlestudy/explore/Xpansion/Candidates/CreateCandidateDialog.tsx b/webapp/src/components/App/Singlestudy/explore/Xpansion/Candidates/CreateCandidateDialog.tsx index 40a4acad23..18366d7fe2 100644 --- a/webapp/src/components/App/Singlestudy/explore/Xpansion/Candidates/CreateCandidateDialog.tsx +++ b/webapp/src/components/App/Singlestudy/explore/Xpansion/Candidates/CreateCandidateDialog.tsx @@ -22,14 +22,14 @@ interface PropType { function CreateCandidateDialog(props: PropType) { const { open, links, onClose, onSave } = props; const [t] = useTranslation(); - const [isToggled, setToggle] = useState(true); + const [isToggled, setIsToggled] = useState(true); //////////////////////////////////////////////////////////////// // Event Handlers //////////////////////////////////////////////////////////////// const handleToggle = () => { - setToggle(!isToggled); + setIsToggled(!isToggled); }; const handleSubmit = (data: SubmitHandlerPlus) => { diff --git a/webapp/src/components/App/Studies/ExportModal/ExportFilter/index.tsx b/webapp/src/components/App/Studies/ExportModal/ExportFilter/index.tsx index e751898f5a..e7a7fc9e85 100644 --- a/webapp/src/components/App/Studies/ExportModal/ExportFilter/index.tsx +++ b/webapp/src/components/App/Studies/ExportModal/ExportFilter/index.tsx @@ -35,7 +35,7 @@ interface PropTypes { function ExportFilterModal(props: PropTypes) { const [t] = useTranslation(); const { output, synthesis, filter, setFilter } = props; - const [year, setCurrentYear] = useState([]); + const [year, setYear] = useState([]); const [byYear, setByYear] = useState<{ isByYear: boolean; nbYear: number }>({ isByYear: false, nbYear: -1, @@ -105,7 +105,7 @@ function ExportFilterModal(props: PropTypes) { }))} data={year.map((elm) => elm.toString())} setValue={(value: string[] | string) => - setCurrentYear((value as string[]).map((elm) => parseInt(elm, 10))) + setYear((value as string[]).map((elm) => parseInt(elm, 10))) } sx={{ width: "100%", mb: 2 }} required diff --git a/webapp/src/components/App/Studies/ExportModal/index.tsx b/webapp/src/components/App/Studies/ExportModal/index.tsx index 83e426358d..0be51dda04 100644 --- a/webapp/src/components/App/Studies/ExportModal/index.tsx +++ b/webapp/src/components/App/Studies/ExportModal/index.tsx @@ -62,7 +62,8 @@ export default function ExportModal(props: BasicDialogProps & Props) { const [optionSelection, setOptionSelection] = useState("exportWith"); const [outputList, setOutputList] = useState(); const [currentOutput, setCurrentOutput] = useState(); - const [synthesis, setStudySynthesis] = useState(); + const [studySynthesis, setStudySynthesis] = + useState(); const [filter, setFilter] = useState({ type: StudyOutputDownloadType.AREAS, level: StudyOutputDownloadLevelDTO.WEEKLY, @@ -206,7 +207,7 @@ export default function ExportModal(props: BasicDialogProps & Props) { ( diff --git a/webapp/src/components/common/GroupedDataTable/utils.ts b/webapp/src/components/common/GroupedDataTable/utils.ts index aad96a3784..df119f0fa8 100644 --- a/webapp/src/components/common/GroupedDataTable/utils.ts +++ b/webapp/src/components/common/GroupedDataTable/utils.ts @@ -20,9 +20,9 @@ export interface TRow { * If the base value is found in the list of existing values, it appends a number * in the format `(n)` to the base value, incrementing `n` until a unique value is found. * - * @param {string} baseValue - The original base value to check. - * @param {string[]} existingValues - The list of existing values to check against. - * @returns {string} A unique value. + * @param baseValue - The original base value to check for uniqueness. + * @param existingValues - The list of existing values to check against for duplicates. + * @returns A unique value derived from the base value by appending a number in parentheses, if necessary. */ export const generateNextValue = ( baseValue: string, @@ -54,14 +54,14 @@ export const generateNextValue = ( * based on the given original value and the existing values in tableData. * * If the property is "name", the function appends " - copy" to the original value. - * If the property is "id", the function uses nameToId to get the base value. + * If the property is "id", the function uses `nameToId` to get the base value. * - * This function leverages generateNextValue to ensure the uniqueness of the value. + * This function leverages `generateNextValue` to ensure the uniqueness of the value. * - * @param {"name" | "id"} property - The property for which the unique value is generated. - * @param {string} originalValue - The original value of the specified property. - * @param {TRow[]} tableData - The existing table data to check against. - * @returns {string} A unique value for the specified property. + * @param property - The property for which the unique value is generated, either "name" or "id". + * @param originalValue - The original value of the specified property. + * @param tableData - The existing table data to check against for ensuring uniqueness. + * @returns A unique value for the specified property. */ export const generateUniqueValue = ( property: "name" | "id", diff --git a/webapp/src/components/common/LogModal.tsx b/webapp/src/components/common/LogModal.tsx index 8dbcdeccb3..74032b95be 100644 --- a/webapp/src/components/common/LogModal.tsx +++ b/webapp/src/components/common/LogModal.tsx @@ -35,7 +35,7 @@ function LogModal(props: Props) { const [logDetail, setLogDetail] = useState(content); const divRef = useRef(null); const logRef = useRef(null); - const [autoscroll, setAutoScroll] = useState(true); + const [autoScroll, setAutoScroll] = useState(true); const [t] = useTranslation(); const updateLog = useCallback( @@ -92,11 +92,11 @@ function LogModal(props: Props) { useEffect(() => { if (logRef.current) { - if (autoscroll) { + if (autoScroll) { scrollToEnd(); } } - }, [logDetail, autoscroll]); + }, [logDetail, autoScroll]); useEffect(() => { if (followLogs) { diff --git a/webapp/src/components/common/SplitLayoutView.tsx b/webapp/src/components/common/SplitLayoutView.tsx index 62c1209e24..5df41be676 100644 --- a/webapp/src/components/common/SplitLayoutView.tsx +++ b/webapp/src/components/common/SplitLayoutView.tsx @@ -8,7 +8,13 @@ interface Props { } /** - * @deprecated Use SplitView instead. + * Renders a split layout view with a fixed left column and a flexible right column. + * This component is deprecated and should be replaced with the `SplitView` component for enhanced functionality and flexibility. + * + * @deprecated Use `SplitView` instead for better layout management and customization options. + * + * @param props - The component props including `left` and `right` components to render in the split layout, and `sx` for styling. + * @returns A React component that displays a split layout with left and right sections. */ function SplitLayoutView(props: Props) { const { left, right, sx } = props; diff --git a/webapp/src/components/common/SplitView/index.tsx b/webapp/src/components/common/SplitView/index.tsx index 3e1249af91..0bb8f86da2 100644 --- a/webapp/src/components/common/SplitView/index.tsx +++ b/webapp/src/components/common/SplitView/index.tsx @@ -11,8 +11,8 @@ export interface SplitViewProps { } /** - * Renders a resizable split view layout. It can be configured - * for both horizontal and vertical directions. + * Renders a resizable split view layout, configurable for both horizontal and vertical directions. + * * @see {@link SplitViewProps} for the properties it accepts. * * @example @@ -20,6 +20,13 @@ export interface SplitViewProps { * * * + * + * @param props - The component props. + * @param props.children - Child components to be rendered within the split views. + * @param props.direction - The orientation of the split view ("horizontal" or "vertical"). + * @param props.sizes - Initial sizes of each view in percentages. The array must sum to 100 and match the number of children. + * @param props.gutterSize - The size of the gutter between split views. Defaults to 4. + * @returns A React component displaying a split layout view with resizable panes. */ function SplitView({ children, diff --git a/webapp/src/hoc/reactHookFormSupport.tsx b/webapp/src/hoc/reactHookFormSupport.tsx index 282ce5358a..a0aec8f2b2 100644 --- a/webapp/src/hoc/reactHookFormSupport.tsx +++ b/webapp/src/hoc/reactHookFormSupport.tsx @@ -67,19 +67,36 @@ export type ReactHookFormSupportProps< shouldUnregister?: never; }; +/** + * Provides React Hook Form support to a field editor component, enhancing it with form control and validation capabilities. + * It integrates custom validation logic, value transformation, and handles form submission state. + * + * @param options - Configuration options for the hook support. + * @param options.preValidate - A function that pre-validates the value before the main validation. + * @param options.setValueAs - A function that transforms the value before setting it into the form. + * @returns A function that takes a field editor component and returns a new component wrapped with React Hook Form functionality. + */ function reactHookFormSupport( options: ReactHookFormSupport = {}, ) { const { preValidate, setValueAs = R.identity } = options; /** - * Wrap in a higher component the specified field editor component + * Wraps the provided field editor component with React Hook Form functionality, + * applying the specified pre-validation and value transformation logic. + * + * @param FieldEditor - The field editor component to wrap. + * @returns The wrapped component with added React Hook Form support. */ function wrapWithReactHookFormSupport< TProps extends FieldEditorProps, >(FieldEditor: React.ComponentType) { /** - * The wrapper component + * The wrapper component that integrates React Hook Form capabilities with the original field editor. + * It manages form control registration, handles value changes and blurring with custom logic, and displays validation errors. + * + * @param props - The props of the field editor, extended with React Hook Form and custom options. + * @returns The field editor component wrapped with React Hook Form functionality. */ function ReactHookFormSupport< TFieldValues extends FieldValues = FieldValues, diff --git a/webapp/src/hooks/useMemoLocked.ts b/webapp/src/hooks/useMemoLocked.ts index 6af425bbc6..b6e03535f9 100644 --- a/webapp/src/hooks/useMemoLocked.ts +++ b/webapp/src/hooks/useMemoLocked.ts @@ -10,6 +10,7 @@ import { useState } from "react"; */ function useMemoLocked(factory: () => T): T { + // eslint-disable-next-line react/hook-use-state const [state] = useState(factory); return state; } diff --git a/webapp/src/hooks/useNavigateOnCondition.ts b/webapp/src/hooks/useNavigateOnCondition.ts index 6930e1ecf6..036ce0e72f 100644 --- a/webapp/src/hooks/useNavigateOnCondition.ts +++ b/webapp/src/hooks/useNavigateOnCondition.ts @@ -23,18 +23,17 @@ interface UseNavigateOnConditionOptions { } /** - * A React hook for conditional navigation using react-router-dom. + * A React hook for conditional navigation using react-router-dom. This hook allows for navigating to a different route + * based on custom logic encapsulated in a `shouldNavigate` function. It observes specified dependencies and triggers navigation + * when they change if the conditions defined in `shouldNavigate` are met. * - * @function - * @name useNavigateOnCondition - * - * @param {Object} options - Configuration options for the hook. - * @param {DependencyList} options.deps - An array of dependencies that the effect will observe. - * @param {To} options.to - The target location to navigate to, it could be a route as a string or a relative numeric location. - * @param {function} [options.shouldNavigate] - An optional function that returns a boolean to determine whether navigation should take place. + * @param options - Configuration options for the hook. + * @param options.deps - An array of dependencies that the effect will observe. + * @param options.to - The target location to navigate to, which can be a route as a string or a relative numeric location. + * @param options.shouldNavigate - An optional function that returns a boolean to determine whether navigation should take place. Defaults to a function that always returns true. * * @example - * - Basic usage + * Basic usage * useNavigateOnCondition({ * deps: [someDependency], * to: '/some-route', diff --git a/webapp/src/services/utils/index.ts b/webapp/src/services/utils/index.ts index 0386ebef79..f7980ee2bd 100644 --- a/webapp/src/services/utils/index.ts +++ b/webapp/src/services/utils/index.ts @@ -155,7 +155,7 @@ export const exportText = (fileData: string, filename: string): void => { * * Ex: '820' -> '8.2' * - * @param v Version in format '[major][minor]0' (ex: '820'). + * @param v - Version in format '[major][minor]0' (ex: '820'). * @returns Version in format '[major].[minor]' (ex: '8.2'). */ export const displayVersionName = (v: string): string => `${v[0]}.${v[1]}`; @@ -248,7 +248,12 @@ export const sortByName = (list: T[]): T[] => { }; /** - * @deprecated This function is deprecated. Please use nameToId instead. + * Converts a name string to an ID format. + * + * @deprecated Please use `nameToId` instead. + * + * @param name - The string to transform. + * @returns The transformed ID string. */ export const transformNameToId = (name: string): string => { let duppl = false; @@ -290,7 +295,8 @@ export const transformNameToId = (name: string): string => { * Converts a name string to a valid ID string. * Replacing any characters that are not alphanumeric or -_,()& with a space, * trimming the resulting string, and converting it to lowercase. - * @param name The name string to convert to an ID. + * + * @param name - The name string to convert to an ID. * @returns The resulting ID string. */ export const nameToId = (name: string): string => { diff --git a/webapp/src/utils/fnUtils.ts b/webapp/src/utils/fnUtils.ts index d232d83246..226e58c836 100644 --- a/webapp/src/utils/fnUtils.ts +++ b/webapp/src/utils/fnUtils.ts @@ -1,6 +1,13 @@ /** - * Use it instead of disabling ESLint rule. + * A utility function designed to be used as a placeholder or stub. It can be used in situations where you might + * otherwise be tempted to disable an ESLint rule temporarily, such as when you need to pass a function that + * does nothing (for example, as a default prop in React components or as a no-operation callback). + * + * By using this function, you maintain code cleanliness and intention clarity without directly suppressing + * linting rules. + * + * @param args - Accepts any number of arguments of any type, but does nothing with them. */ export function voidFn(...args: TArgs) { - // Do nothing + // Intentionally empty, as its purpose is to do nothing. } diff --git a/webapp/src/utils/stringUtils.ts b/webapp/src/utils/stringUtils.ts index 1b83ea6b6d..823829fb29 100644 --- a/webapp/src/utils/stringUtils.ts +++ b/webapp/src/utils/stringUtils.ts @@ -11,9 +11,14 @@ export const isSearchMatching = R.curry( ); /** - * Formats a string with values. + * Formats a string by replacing placeholders with specified values. + * + * @param str - The string containing placeholders in the format `{placeholder}`. + * @param values - An object mapping placeholders to their replacement values. + * @returns The formatted string with all placeholders replaced by their corresponding values. + * * @example - * format("Hello {name}", { name: "John" }); // returns "Hello John" + * format("Hello {name}", { name: "John" }); // Returns: "Hello John" */ export function format(str: string, values: Record): string { return str.replace(/{([a-zA-Z0-9]+)}/g, (_, key) => values[key]);