From 30f2a1790ae10fd302e46a97a3c0ad1b386c5d87 Mon Sep 17 00:00:00 2001 From: LE SAULNIER Kevin Date: Fri, 21 Feb 2025 15:27:10 +0100 Subject: [PATCH 1/6] fix: 1st commit Signed-off-by: LE SAULNIER Kevin --- .../custom-aggrid-boolean-filter.tsx | 2 +- .../custom-aggrid-text-filter.tsx | 4 +- src/components/diagrams/diagram-header.tsx | 5 +- src/components/diagrams/diagram-pane.tsx | 18 +- .../diagrams/diagram-resizable-box.tsx | 6 +- .../network-area-diagram-content.tsx | 2 +- .../singleLineDiagram/position-diagram.tsx | 3 +- .../single-line-diagram-content.tsx | 3 +- src/components/dialogs/commons/formFiller.jsx | 2 +- .../commons/modification-dialog-content.jsx | 2 +- .../dialogs/limits/temporary-limits-table.tsx | 5 - .../measurements/power-with-validity-form.tsx | 2 - .../vsc/hvdc-line-pane/vsc-hvdc-line-pane.tsx | 2 - .../tabular-creation-form.tsx | 3 +- .../tabular-modification-form.tsx | 3 +- .../dynamic-security-analysis-parameters.tsx | 3 +- .../curve/dialog/curve-selector-dialog.tsx | 2 +- .../dynamic-simulation-parameters.tsx | 3 +- .../loadflow/load-flow-parameters.tsx | 2 +- .../network-visualizations-parameters.tsx | 2 +- .../non-evacuated-energy-parameters.tsx | 3 +- .../security-analysis-parameters.tsx | 2 +- .../sensi/sensitivity-analysis-parameters.tsx | 2 +- .../parameters/short-circuit-parameters.tsx | 2 +- .../voltageinit/voltage-init-parameters.tsx | 2 +- .../menus/operating-status-menu.tsx | 6 +- src/components/result-view-tab.tsx | 2 +- .../results/common/results-global-filter.tsx | 7 +- .../dynamic-simulation-result-chart.jsx | 3 +- ...dynamic-simulation-result-series-chart.jsx | 2 +- .../results/loadflow/load-flow-result-tab.tsx | 3 +- .../security-analysis.type.ts | 11 +- src/components/spreadsheet/table-wrapper.tsx | 3 +- .../spreadsheet/utils/cell-renderers.tsx | 2 +- src/components/study-container.jsx | 3 +- src/components/tooltips/equipment-popover.jsx | 3 +- ...node.jsx => alert-custom-message-node.tsx} | 24 +- src/components/utils/ask-text-dialog.tsx | 2 +- ...nested-menu.jsx => custom-nested-menu.tsx} | 18 +- ...nation.jsx => custom-table-pagination.tsx} | 15 +- ...fosHandler.js => equipmentInfosHandler.ts} | 10 +- src/components/utils/functions.js | 8 - src/components/utils/inputs/input-hooks.jsx | 384 ------------------ src/components/utils/inputs/input-hooks.tsx | 239 +++++++++++ ...g-hook.js => is-any-node-building-hook.ts} | 5 +- .../utils/list-item-with-delete-button.jsx | 68 ---- ...th-overlay.jsx => loader-with-overlay.tsx} | 11 +- ...es-hook.js => localized-countries-hook.ts} | 10 +- .../utils/remote-resource-handler.js | 42 -- ...e-input.jsx => boolean-nullable-input.tsx} | 35 +- ...p-items-input.jsx => chip-items-input.tsx} | 16 +- ...-input.jsx => country-selection-input.tsx} | 12 +- .../{enum-input.jsx => enum-input.tsx} | 13 +- .../expandable-input/expandable-input.tsx | 3 +- ...y-input.jsx => button-read-only-input.tsx} | 8 +- ...ead-only-input.jsx => read-only-input.tsx} | 7 +- ...al-input.jsx => table-numerical-input.tsx} | 26 +- ...le-text-input.jsx => table-text-input.tsx} | 14 +- .../{split-button.jsx => split-button.tsx} | 46 ++- .../utils/{utils.test.js => utils.test.ts} | 0 src/components/utils/{utils.js => utils.ts} | 123 +++--- ...s.test.js => validation-functions.test.ts} | 96 ----- ...n-functions.js => validation-functions.ts} | 82 +--- ...{waiting-loader.jsx => waiting-loader.tsx} | 9 +- 64 files changed, 551 insertions(+), 905 deletions(-) rename src/components/utils/{alert-custom-message-node.jsx => alert-custom-message-node.tsx} (58%) rename src/components/utils/{custom-nested-menu.jsx => custom-nested-menu.tsx} (67%) rename src/components/utils/{custom-table-pagination.jsx => custom-table-pagination.tsx} (87%) rename src/components/utils/{equipmentInfosHandler.js => equipmentInfosHandler.ts} (76%) delete mode 100644 src/components/utils/functions.js delete mode 100644 src/components/utils/inputs/input-hooks.jsx create mode 100644 src/components/utils/inputs/input-hooks.tsx rename src/components/utils/{is-any-node-building-hook.js => is-any-node-building-hook.ts} (72%) delete mode 100644 src/components/utils/list-item-with-delete-button.jsx rename src/components/utils/{loader-with-overlay.jsx => loader-with-overlay.tsx} (76%) rename src/components/utils/{localized-countries-hook.js => localized-countries-hook.ts} (83%) delete mode 100644 src/components/utils/remote-resource-handler.js rename src/components/utils/rhf-inputs/{boolean-nullable-input.jsx => boolean-nullable-input.tsx} (70%) rename src/components/utils/rhf-inputs/{chip-items-input.jsx => chip-items-input.tsx} (90%) rename src/components/utils/rhf-inputs/{country-selection-input.jsx => country-selection-input.tsx} (60%) rename src/components/utils/rhf-inputs/{enum-input.jsx => enum-input.tsx} (82%) rename src/components/utils/rhf-inputs/read-only/{button-read-only-input.jsx => button-read-only-input.tsx} (90%) rename src/components/utils/rhf-inputs/read-only/{read-only-input.jsx => read-only-input.tsx} (86%) rename src/components/utils/rhf-inputs/table-inputs/{table-numerical-input.jsx => table-numerical-input.tsx} (80%) rename src/components/utils/rhf-inputs/table-inputs/{table-text-input.jsx => table-text-input.tsx} (74%) rename src/components/utils/{split-button.jsx => split-button.tsx} (87%) rename src/components/utils/{utils.test.js => utils.test.ts} (100%) rename src/components/utils/{utils.js => utils.ts} (66%) rename src/components/utils/{validation-functions.test.js => validation-functions.test.ts} (82%) rename src/components/utils/{validation-functions.js => validation-functions.ts} (63%) rename src/components/utils/{waiting-loader.jsx => waiting-loader.tsx} (85%) diff --git a/src/components/custom-aggrid/custom-aggrid-filters/custom-aggrid-boolean-filter.tsx b/src/components/custom-aggrid/custom-aggrid-filters/custom-aggrid-boolean-filter.tsx index eb83fa8625..70015373f9 100644 --- a/src/components/custom-aggrid/custom-aggrid-filters/custom-aggrid-boolean-filter.tsx +++ b/src/components/custom-aggrid/custom-aggrid-filters/custom-aggrid-boolean-filter.tsx @@ -10,10 +10,10 @@ import { IconButton, MenuItem, Select } from '@mui/material'; import ClearIcon from '@mui/icons-material/Clear'; import { useIntl } from 'react-intl'; import { SelectChangeEvent } from '@mui/material/Select/SelectInput'; -import { mergeSx } from 'components/utils/functions'; import { useCustomAggridFilter } from './hooks/use-custom-aggrid-filter'; import { CustomAggridFilterParams, FILTER_DATA_TYPES, FILTER_TEXT_COMPARATORS } from '../custom-aggrid-header.type'; import { isNonEmptyStringOrArray } from '../../../utils/types-utils'; +import { mergeSx } from '@gridsuite/commons-ui'; export enum BooleanFilterValue { TRUE = 'true', diff --git a/src/components/custom-aggrid/custom-aggrid-filters/custom-aggrid-text-filter.tsx b/src/components/custom-aggrid/custom-aggrid-filters/custom-aggrid-text-filter.tsx index 474a2f6929..0fcd12a96c 100644 --- a/src/components/custom-aggrid/custom-aggrid-filters/custom-aggrid-text-filter.tsx +++ b/src/components/custom-aggrid/custom-aggrid-filters/custom-aggrid-text-filter.tsx @@ -9,8 +9,8 @@ import { TextField, Grid, InputAdornment, IconButton } from '@mui/material'; import ClearIcon from '@mui/icons-material/Clear'; import { DisplayRounding } from '../display-rounding'; import { FILTER_DATA_TYPES } from '../custom-aggrid-header.type'; -import { mergeSx } from '../../utils/functions'; import { useIntl } from 'react-intl'; +import { mergeSx } from '@gridsuite/commons-ui'; const styles = { input: { @@ -60,7 +60,7 @@ export const CustomAggridTextFilter: React.FC = ({ inputProps={{ type: isNumberInput ? FILTER_DATA_TYPES.NUMBER : FILTER_DATA_TYPES.TEXT, }} - sx={mergeSx(styles.input, isNumberInput && styles.noArrows)} + sx={mergeSx(styles.input, isNumberInput ? styles.noArrows : undefined)} InputProps={{ endAdornment: value ? ( diff --git a/src/components/diagrams/diagram-header.tsx b/src/components/diagrams/diagram-header.tsx index 75e84cc0e3..81eb98a87e 100644 --- a/src/components/diagrams/diagram-header.tsx +++ b/src/components/diagrams/diagram-header.tsx @@ -8,14 +8,13 @@ import { useCallback, useState, useEffect } from 'react'; import { useDispatch, useSelector } from 'react-redux'; import Box from '@mui/material/Box'; -import { OverflowableText } from '@gridsuite/commons-ui'; +import { mergeSx, OverflowableText } from '@gridsuite/commons-ui'; import IconButton from '@mui/material/IconButton'; import MinimizeIcon from '@mui/icons-material/Minimize'; import PushPinIcon from '@mui/icons-material/PushPin'; import PushPinOutlinedIcon from '@mui/icons-material/PushPinOutlined'; import CloseIcon from '@mui/icons-material/Close'; import { stopDiagramBlink } from '../../redux/actions'; -import { mergeSx } from '../utils/functions'; import { Theme } from '@mui/material'; import { AppState } from 'redux/reducer'; @@ -121,7 +120,7 @@ const DiagramHeader: React.FC = ({ */ return ( - + ({ - name: getNameOrId(vl), + name: getNameOrId({ name: vl.name, id: vl.substationId }), substationId: vl.substationId, })) .sort( ( - vlA: { name: string; substationId: UUID }, - vlB: { name: string; substationId: UUID } - ) => vlA.name.toLowerCase().localeCompare(vlB.name.toLowerCase()) + vlA: { name?: string; substationId: UUID }, + vlB: { name?: string; substationId: UUID } + ) => vlA.name?.toLowerCase().localeCompare(vlB.name?.toLowerCase() ?? '') || 0 ) - .forEach((voltageLevel: { name: string; substationId: UUID }) => { + .forEach((voltageLevel: { name?: string; substationId: UUID }) => { const name = voltageLevel.name; if (name !== null) { nadTitle += (nadTitle !== '' ? ', ' : '') + name; @@ -1047,7 +1046,10 @@ export function DiagramPane({ {({ width, height }) => ( = ({ resizeHandles={align === 'right' ? ['sw'] : undefined} sx={mergeSx( styles.resizable, - !disableResize && align === 'right' && styles.leftHandle, - !disableResize && align === 'left' && styles.rightHandle + !disableResize && align === 'right' ? styles.leftHandle : undefined, + !disableResize && align === 'left' ? styles.rightHandle : undefined )} > <> diff --git a/src/components/diagrams/networkAreaDiagram/network-area-diagram-content.tsx b/src/components/diagrams/networkAreaDiagram/network-area-diagram-content.tsx index 3cfbffeea8..331fa20d7e 100644 --- a/src/components/diagrams/networkAreaDiagram/network-area-diagram-content.tsx +++ b/src/components/diagrams/networkAreaDiagram/network-area-diagram-content.tsx @@ -25,7 +25,6 @@ import { } from '@powsybl/network-viewer'; import LinearProgress from '@mui/material/LinearProgress'; import Box from '@mui/material/Box'; -import { mergeSx } from '../../utils/functions'; import ComputingType from '../../computing-status/computing-type'; import { AppState, NadNodeMovement, NadTextMovement } from 'redux/reducer'; import { storeNetworkAreaDiagramNodeMovement, storeNetworkAreaDiagramTextNodeMovement } from '../../../redux/actions'; @@ -35,6 +34,7 @@ import { UUID } from 'crypto'; import { Point } from '@svgdotjs/svg.js'; import { EQUIPMENT_TYPES } from 'components/utils/equipment-types'; import { FEEDER_TYPES } from 'components/utils/feederType'; +import { mergeSx } from '@gridsuite/commons-ui'; const dynamicCssRules: CSS_RULE[] = [ { diff --git a/src/components/diagrams/singleLineDiagram/position-diagram.tsx b/src/components/diagrams/singleLineDiagram/position-diagram.tsx index 72983f5234..007f27e9e9 100644 --- a/src/components/diagrams/singleLineDiagram/position-diagram.tsx +++ b/src/components/diagrams/singleLineDiagram/position-diagram.tsx @@ -13,11 +13,10 @@ import { useTheme } from '@mui/material/styles'; import LinearProgress from '@mui/material/LinearProgress'; import { SingleLineDiagramViewer, SLDMetadata } from '@powsybl/network-viewer'; import { styles, MAX_HEIGHT_VOLTAGE_LEVEL, MAX_WIDTH_VOLTAGE_LEVEL, NoSvg, MIN_WIDTH, Svg } from '../diagram-common'; -import { useIntlRef, useSnackMessage } from '@gridsuite/commons-ui'; +import { mergeSx, useIntlRef, useSnackMessage } from '@gridsuite/commons-ui'; import { Paper } from '@mui/material'; import DiagramHeader from '../diagram-header'; import { fetchSvg } from '../../../services/study'; -import { mergeSx } from '../../utils/functions'; import { AppState } from 'redux/reducer'; interface PositionDiagramProps { diff --git a/src/components/diagrams/singleLineDiagram/single-line-diagram-content.tsx b/src/components/diagrams/singleLineDiagram/single-line-diagram-content.tsx index e2ff9e9479..30137e1cf2 100644 --- a/src/components/diagrams/singleLineDiagram/single-line-diagram-content.tsx +++ b/src/components/diagrams/singleLineDiagram/single-line-diagram-content.tsx @@ -28,7 +28,7 @@ import { isNodeReadOnly } from '../../graph/util/model-functions'; import { useIsAnyNodeBuilding } from '../../utils/is-any-node-building-hook'; import Alert from '@mui/material/Alert'; import { useTheme } from '@mui/material/styles'; -import { EquipmentType, useSnackMessage } from '@gridsuite/commons-ui'; +import { EquipmentType, mergeSx, useSnackMessage } from '@gridsuite/commons-ui'; import Box from '@mui/material/Box'; import LinearProgress from '@mui/material/LinearProgress'; import GeneratorModificationDialog from 'components/dialogs/network-modifications/generator/modification/generator-modification-dialog'; @@ -47,7 +47,6 @@ import { EQUIPMENT_INFOS_TYPES, EQUIPMENT_TYPES, convertToEquipmentType } from ' import EquipmentDeletionDialog from '../../dialogs/network-modifications/equipment-deletion/equipment-deletion-dialog'; import { startShortCircuitAnalysis } from '../../../services/study/short-circuit-analysis'; import { fetchNetworkElementInfos } from '../../../services/study/network'; -import { mergeSx } from '../../utils/functions'; import { useOneBusShortcircuitAnalysisLoader } from '../use-one-bus-shortcircuit-analysis-loader'; import { DynamicSimulationEventDialog } from '../../dialogs/dynamicsimulation/event/dynamic-simulation-event-dialog'; import { setComputationStarting, setComputingStatus, setLogsFilter } from '../../../redux/actions'; diff --git a/src/components/dialogs/commons/formFiller.jsx b/src/components/dialogs/commons/formFiller.jsx index e026196582..f66ba2cba5 100644 --- a/src/components/dialogs/commons/formFiller.jsx +++ b/src/components/dialogs/commons/formFiller.jsx @@ -5,9 +5,9 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +import { mergeSx } from '@gridsuite/commons-ui'; import { Box, Grid } from '@mui/material'; import { useTheme } from '@mui/material/styles'; -import { mergeSx } from '../../utils/functions'; const styles = { filler: (theme) => ({ diff --git a/src/components/dialogs/commons/modification-dialog-content.jsx b/src/components/dialogs/commons/modification-dialog-content.jsx index 1ac599db5f..732ff2c987 100644 --- a/src/components/dialogs/commons/modification-dialog-content.jsx +++ b/src/components/dialogs/commons/modification-dialog-content.jsx @@ -8,7 +8,7 @@ import { FormattedMessage } from 'react-intl'; import { Grid, Dialog, DialogTitle, DialogContent, DialogActions, LinearProgress } from '@mui/material'; import PropTypes from 'prop-types'; -import { useButtonWithTooltip } from '../../utils/inputs/input-hooks'; +import { useButtonWithTooltip } from '../../utils/inputs/input-hooks.tsx'; import FindInPageIcon from '@mui/icons-material/FindInPage'; import AutoStoriesOutlinedIcon from '@mui/icons-material/AutoStoriesOutlined'; import { useSelector } from 'react-redux'; diff --git a/src/components/dialogs/limits/temporary-limits-table.tsx b/src/components/dialogs/limits/temporary-limits-table.tsx index ecf0690039..f6f9927739 100644 --- a/src/components/dialogs/limits/temporary-limits-table.tsx +++ b/src/components/dialogs/limits/temporary-limits-table.tsx @@ -41,9 +41,6 @@ function EditableTableCell({ name, column, previousValue, valueModified, ...prop {column.numeric ? ( ) : ( = ({ id, name={`${id}.${VALIDITY}`} label="ValidMeasurement" previousValue={previousValidityField} - id={undefined} - formProps={undefined} /> ); diff --git a/src/components/dialogs/network-modifications/hvdc-line/vsc/hvdc-line-pane/vsc-hvdc-line-pane.tsx b/src/components/dialogs/network-modifications/hvdc-line/vsc/hvdc-line-pane/vsc-hvdc-line-pane.tsx index e099b171b6..94b0e69dc9 100644 --- a/src/components/dialogs/network-modifications/hvdc-line/vsc/hvdc-line-pane/vsc-hvdc-line-pane.tsx +++ b/src/components/dialogs/network-modifications/hvdc-line/vsc/hvdc-line-pane/vsc-hvdc-line-pane.tsx @@ -147,8 +147,6 @@ const VscHvdcLinePane: FunctionComponent = ({ name={`${id}.${ANGLE_DROOP_ACTIVE_POWER_CONTROL}`} label={'angleDroopActivePowerControlLabel'} previousValue={previousAngleDropPowerControl()} - id={undefined} - formProps={undefined} /> ); } else { diff --git a/src/components/dialogs/network-modifications/tabular-creation/tabular-creation-form.tsx b/src/components/dialogs/network-modifications/tabular-creation/tabular-creation-form.tsx index 92cef94abc..1f35fb9bf1 100644 --- a/src/components/dialogs/network-modifications/tabular-creation/tabular-creation-form.tsx +++ b/src/components/dialogs/network-modifications/tabular-creation/tabular-creation-form.tsx @@ -18,7 +18,6 @@ import { FREQUENCY_REGULATION, } from 'components/utils/field-constants'; import { EQUIPMENT_TYPES } from 'components/utils/equipment-types'; -import { useCSVPicker } from 'components/utils/inputs/input-hooks'; import CsvDownloader from 'react-csv-downloader'; import { Alert, Button, Grid } from '@mui/material'; import { TABULAR_CREATION_FIELDS, styles, TabularCreationField } from './tabular-creation-utils'; @@ -26,6 +25,7 @@ import { BooleanNullableCellRenderer, DefaultCellRenderer } from 'components/spr import Papa from 'papaparse'; import { ColDef } from 'ag-grid-community'; import GridItem from '../../commons/grid-item'; +import { useCSVPicker } from 'components/utils/inputs/input-hooks'; const TabularCreationForm = () => { const intl = useIntl(); @@ -108,7 +108,6 @@ const TabularCreationForm = () => { const [selectedFile, FileField, selectedFileError] = useCSVPicker({ label: 'ImportCreations', header: csvColumns, - maxTapNumber: undefined, disabled: !csvColumns, resetTrigger: typeChangedTrigger, }); diff --git a/src/components/dialogs/network-modifications/tabular-modification/tabular-modification-form.tsx b/src/components/dialogs/network-modifications/tabular-modification/tabular-modification-form.tsx index 3860bae3ab..692cad8606 100644 --- a/src/components/dialogs/network-modifications/tabular-modification/tabular-modification-form.tsx +++ b/src/components/dialogs/network-modifications/tabular-modification/tabular-modification-form.tsx @@ -19,7 +19,6 @@ import { VOLTAGE_REGULATION_ON, } from 'components/utils/field-constants'; import { EQUIPMENT_TYPES } from 'components/utils/equipment-types'; -import { useCSVPicker } from 'components/utils/inputs/input-hooks'; import CsvDownloader from 'react-csv-downloader'; import { Alert, Button, Grid } from '@mui/material'; import { TABULAR_MODIFICATION_FIELDS, styles } from './tabular-modification-utils'; @@ -27,6 +26,7 @@ import { BooleanNullableCellRenderer, DefaultCellRenderer } from 'components/spr import Papa from 'papaparse'; import { ColDef } from 'ag-grid-community'; import GridItem from '../../commons/grid-item'; +import { useCSVPicker } from 'components/utils/inputs/input-hooks'; const TabularModificationForm = () => { const intl = useIntl(); @@ -102,7 +102,6 @@ const TabularModificationForm = () => { const [selectedFile, FileField, selectedFileError] = useCSVPicker({ label: 'ImportModifications', header: csvColumns, - maxTapNumber: undefined, disabled: !csvColumns, resetTrigger: typeChangedTrigger, }); diff --git a/src/components/dialogs/parameters/dynamic-security-analysis/dynamic-security-analysis-parameters.tsx b/src/components/dialogs/parameters/dynamic-security-analysis/dynamic-security-analysis-parameters.tsx index 97622bc028..cd72484ba4 100644 --- a/src/components/dialogs/parameters/dynamic-security-analysis/dynamic-security-analysis-parameters.tsx +++ b/src/components/dialogs/parameters/dynamic-security-analysis/dynamic-security-analysis-parameters.tsx @@ -24,8 +24,7 @@ import { } from '../../../../services/study/dynamic-security-analysis'; import { OptionalServicesNames } from '../../../utils/optional-services'; import { useOptionalServiceStatus } from '../../../../hooks/use-optional-service-status'; -import { mergeSx } from '../../../utils/functions'; -import { CustomFormProvider, isObjectEmpty, SubmitButton } from '@gridsuite/commons-ui'; +import { CustomFormProvider, isObjectEmpty, mergeSx, SubmitButton } from '@gridsuite/commons-ui'; import { yupResolver } from '@hookform/resolvers/yup'; import { FieldErrors, useForm } from 'react-hook-form'; import { getTabStyle } from '../../../utils/tab-utils'; diff --git a/src/components/dialogs/parameters/dynamicsimulation/curve/dialog/curve-selector-dialog.tsx b/src/components/dialogs/parameters/dynamicsimulation/curve/dialog/curve-selector-dialog.tsx index 1a4874ed96..41868ecd6b 100644 --- a/src/components/dialogs/parameters/dynamicsimulation/curve/dialog/curve-selector-dialog.tsx +++ b/src/components/dialogs/parameters/dynamicsimulation/curve/dialog/curve-selector-dialog.tsx @@ -24,7 +24,7 @@ import CurvePreview, { Curve, CurveHandler } from './curve-preview'; import Tooltip from '@mui/material/Tooltip'; import ArrowCircleRightIcon from '@mui/icons-material/ArrowCircleRight'; import ArrowCircleLeftIcon from '@mui/icons-material/ArrowCircleLeft'; -import { mergeSx } from '../../../../../utils/functions'; +import { mergeSx } from '@gridsuite/commons-ui'; interface CurveSelectorDialogProps { open: boolean; diff --git a/src/components/dialogs/parameters/dynamicsimulation/dynamic-simulation-parameters.tsx b/src/components/dialogs/parameters/dynamicsimulation/dynamic-simulation-parameters.tsx index db36f17260..f25c8e7faa 100644 --- a/src/components/dialogs/parameters/dynamicsimulation/dynamic-simulation-parameters.tsx +++ b/src/components/dialogs/parameters/dynamicsimulation/dynamic-simulation-parameters.tsx @@ -48,8 +48,7 @@ import { } from '../../../../services/study/dynamic-simulation'; import { OptionalServicesNames } from '../../../utils/optional-services'; import { useOptionalServiceStatus } from '../../../../hooks/use-optional-service-status'; -import { mergeSx } from '../../../utils/functions'; -import { CustomFormProvider, isObjectEmpty, SubmitButton } from '@gridsuite/commons-ui'; +import { CustomFormProvider, isObjectEmpty, mergeSx, SubmitButton } from '@gridsuite/commons-ui'; import { yupResolver } from '@hookform/resolvers/yup'; import { FieldErrors, useForm } from 'react-hook-form'; import { getTabStyle } from '../../../utils/tab-utils'; diff --git a/src/components/dialogs/parameters/loadflow/load-flow-parameters.tsx b/src/components/dialogs/parameters/loadflow/load-flow-parameters.tsx index 50e651aed7..4add76867f 100644 --- a/src/components/dialogs/parameters/loadflow/load-flow-parameters.tsx +++ b/src/components/dialogs/parameters/loadflow/load-flow-parameters.tsx @@ -18,11 +18,11 @@ import { import { Box, Grid } from '@mui/material'; import { LabelledButton, styles, useParameterState } from '../parameters'; import { FormattedMessage, useIntl } from 'react-intl'; -import { mergeSx } from '../../../utils/functions'; import { CustomFormProvider, DirectoryItemSelector, ElementType, + mergeSx, SubmitButton, TreeViewFinderNodeProps, useSnackMessage, diff --git a/src/components/dialogs/parameters/network-visualizations/network-visualizations-parameters.tsx b/src/components/dialogs/parameters/network-visualizations/network-visualizations-parameters.tsx index 115dc3f24b..692650d4a6 100644 --- a/src/components/dialogs/parameters/network-visualizations/network-visualizations-parameters.tsx +++ b/src/components/dialogs/parameters/network-visualizations/network-visualizations-parameters.tsx @@ -23,12 +23,12 @@ import { CustomFormProvider, DirectoryItemSelector, ElementType, + mergeSx, SubmitButton, TreeViewFinderNodeProps, useSnackMessage, } from '@gridsuite/commons-ui'; import { TabValue } from './network-visualizations-utils'; -import { mergeSx } from '../../../utils/functions'; import { NetworkVisualizationParameters } from './network-visualizations.types'; import { getNetworkVisualizationParameters, diff --git a/src/components/dialogs/parameters/non-evacuated-energy/non-evacuated-energy-parameters.tsx b/src/components/dialogs/parameters/non-evacuated-energy/non-evacuated-energy-parameters.tsx index 5633a87395..ce94b5c9e0 100644 --- a/src/components/dialogs/parameters/non-evacuated-energy/non-evacuated-energy-parameters.tsx +++ b/src/components/dialogs/parameters/non-evacuated-energy/non-evacuated-energy-parameters.tsx @@ -5,7 +5,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -import { CustomFormProvider, MuiSelectInput, SubmitButton, useSnackMessage } from '@gridsuite/commons-ui'; +import { CustomFormProvider, mergeSx, MuiSelectInput, SubmitButton, useSnackMessage } from '@gridsuite/commons-ui'; import { Button, DialogActions, Grid } from '@mui/material'; import { FunctionComponent, useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { FormattedMessage } from 'react-intl'; @@ -63,7 +63,6 @@ import { getMonitoredBranchesFormSchema, getMonitoredBranchesParams, } from './utils'; -import { mergeSx } from 'components/utils/functions'; import ComputingType from '../../../computing-status/computing-type'; import { isComputationParametersUpdated } from '../common/computation-parameters-util'; import { OptionalServicesNames, OptionalServicesStatus } from 'components/utils/optional-services'; diff --git a/src/components/dialogs/parameters/security-analysis/security-analysis-parameters.tsx b/src/components/dialogs/parameters/security-analysis/security-analysis-parameters.tsx index 7b475d59ad..31a77cc181 100644 --- a/src/components/dialogs/parameters/security-analysis/security-analysis-parameters.tsx +++ b/src/components/dialogs/parameters/security-analysis/security-analysis-parameters.tsx @@ -9,11 +9,11 @@ import React, { Dispatch, FunctionComponent, SetStateAction, useCallback, useEff import { Box, Grid } from '@mui/material'; import { LabelledButton, styles } from '../parameters'; import { FormattedMessage, useIntl } from 'react-intl'; -import { mergeSx } from '../../../utils/functions'; import { CustomFormProvider, DirectoryItemSelector, ElementType, + mergeSx, MuiSelectInput, SubmitButton, TreeViewFinderNodeProps, diff --git a/src/components/dialogs/parameters/sensi/sensitivity-analysis-parameters.tsx b/src/components/dialogs/parameters/sensi/sensitivity-analysis-parameters.tsx index d58c259f97..cbc7b8d235 100644 --- a/src/components/dialogs/parameters/sensi/sensitivity-analysis-parameters.tsx +++ b/src/components/dialogs/parameters/sensi/sensitivity-analysis-parameters.tsx @@ -9,6 +9,7 @@ import { CustomFormProvider, DirectoryItemSelector, ElementType, + mergeSx, MuiSelectInput, SubmitButton, TreeViewFinderNodeProps, @@ -70,7 +71,6 @@ import { getSensiPSTsFormSchema, IRowNewParams, } from './utils'; -import { mergeSx } from 'components/utils/functions'; import CreateParameterDialog from '../common/parameters-creation-dialog'; import LineSeparator from '../../commons/line-separator'; import { AppState } from 'redux/reducer'; diff --git a/src/components/dialogs/parameters/short-circuit-parameters.tsx b/src/components/dialogs/parameters/short-circuit-parameters.tsx index c660630600..7ef2e731f9 100644 --- a/src/components/dialogs/parameters/short-circuit-parameters.tsx +++ b/src/components/dialogs/parameters/short-circuit-parameters.tsx @@ -13,6 +13,7 @@ import { CustomFormProvider, DirectoryItemSelector, ElementType, + mergeSx, SubmitButton, TreeViewFinderNodeProps, useSnackMessage, @@ -26,7 +27,6 @@ import { import { fetchShortCircuitParameters } from '../../../services/short-circuit-analysis'; import { OptionalServicesNames, OptionalServicesStatus } from '../../utils/optional-services'; import { useOptionalServiceStatus } from '../../../hooks/use-optional-service-status'; -import { mergeSx } from '../../utils/functions'; import yup from '../../utils/yup-config'; import { SHORT_CIRCUIT_INITIAL_VOLTAGE_PROFILE_MODE, diff --git a/src/components/dialogs/parameters/voltageinit/voltage-init-parameters.tsx b/src/components/dialogs/parameters/voltageinit/voltage-init-parameters.tsx index 689e01be46..87e77d4b28 100644 --- a/src/components/dialogs/parameters/voltageinit/voltage-init-parameters.tsx +++ b/src/components/dialogs/parameters/voltageinit/voltage-init-parameters.tsx @@ -12,6 +12,7 @@ import { useSnackMessage, CustomFormProvider, TreeViewFinderNodeProps, + mergeSx, } from '@gridsuite/commons-ui'; import { Button, DialogActions, Grid, Tab, Tabs } from '@mui/material'; import { Dispatch, SetStateAction, SyntheticEvent, useCallback, useEffect, useState } from 'react'; @@ -31,7 +32,6 @@ import { fromVoltageInitParamsDataToFormValues, } from './voltage-init-utils'; import { getVoltageInitParameters } from 'services/voltage-init'; -import { mergeSx } from 'components/utils/functions'; import { GeneralParameters } from './general-parameters'; import { DEFAULT_GENERAL_APPLY_MODIFICATIONS, diff --git a/src/components/menus/operating-status-menu.tsx b/src/components/menus/operating-status-menu.tsx index fa8381c766..a617626a1f 100644 --- a/src/components/menus/operating-status-menu.tsx +++ b/src/components/menus/operating-status-menu.tsx @@ -234,7 +234,7 @@ const withOperatingStatusMenu = )} handleTrip()} + onClick={handleTrip} disabled={!isNodeEditable || equipmentInfos?.operatingStatus === OperatingStatus.FORCED_OUTAGE} > @@ -284,7 +284,7 @@ const withOperatingStatusMenu = { substation: getNameOrId({ name: equipmentInfos?.voltageLevelName1, - id: equipmentInfos?.voltageLevelId1, + id: equipmentInfos?.voltageLevelId1 ?? '', }), } )} @@ -316,7 +316,7 @@ const withOperatingStatusMenu = { substation: getNameOrId({ name: equipmentInfos?.voltageLevelName2, - id: equipmentInfos?.voltageLevelId2, + id: equipmentInfos?.voltageLevelId2 ?? '', }), } )} diff --git a/src/components/result-view-tab.tsx b/src/components/result-view-tab.tsx index da57152347..797b18f54f 100644 --- a/src/components/result-view-tab.tsx +++ b/src/components/result-view-tab.tsx @@ -25,10 +25,10 @@ import { SecurityAnalysisResultTab } from './results/securityanalysis/security-a import { LoadFlowResultTab } from './results/loadflow/load-flow-result-tab'; import ComputingType from './computing-status/computing-type'; import { useSelector } from 'react-redux'; -import { usePrevious } from './utils/utils'; import { Box, Paper, Tab, Tabs } from '@mui/material'; import { StateEstimationResultTab } from './results/stateestimation/state-estimation-result-tab'; import DynamicSecurityAnalysisResultTab from './results/dynamic-security-analysis/dynamic-security-analysis-result-tab'; +import { usePrevious } from '@gridsuite/commons-ui'; const styles = { table: { diff --git a/src/components/results/common/results-global-filter.tsx b/src/components/results/common/results-global-filter.tsx index 97c0f608e0..d428b64d0c 100644 --- a/src/components/results/common/results-global-filter.tsx +++ b/src/components/results/common/results-global-filter.tsx @@ -9,12 +9,12 @@ import { FunctionComponent, SyntheticEvent, useCallback, useState } from 'react' import { Autocomplete, Box, Chip, FilterOptionsState, InputAdornment, TextField, Theme } from '@mui/material'; import { FilterAlt } from '@mui/icons-material'; import { FormattedMessage, useIntl } from 'react-intl'; -import { mergeSx } from '../../utils/functions'; import { useLocalizedCountries } from 'components/utils/localized-countries-hook'; import { useDispatch, useSelector } from 'react-redux'; import { addToRecentGlobalFilters } from '../../../redux/actions'; import { AppState } from '../../../redux/reducer'; import { AppDispatch } from '../../../redux/store'; +import { mergeSx } from '@gridsuite/commons-ui'; const styles = { autocomplete: (theme: Theme) => ({ @@ -214,7 +214,10 @@ const ResultsGlobalFilter: FunctionComponent = ({ onCh const recent: boolean = group === recentFilter; const numOfGroupOptions: number = numberOfOptions.get(group) ?? 0; return ( - + diff --git a/src/components/results/dynamicsimulation/timeseries/dynamic-simulation-result-chart.jsx b/src/components/results/dynamicsimulation/timeseries/dynamic-simulation-result-chart.jsx index f5f670dfec..b3f295b70a 100644 --- a/src/components/results/dynamicsimulation/timeseries/dynamic-simulation-result-chart.jsx +++ b/src/components/results/dynamicsimulation/timeseries/dynamic-simulation-result-chart.jsx @@ -22,8 +22,7 @@ import FitScreenSharpIcon from '@mui/icons-material/FitScreenSharp'; import FullscreenExitSharpIcon from '@mui/icons-material/FullscreenExitSharp'; import ResponsiveGridLayout from '../common/gridlayout/responsive-grid-layout'; import { lighten } from '@mui/material/styles'; -import { useDebounce } from '@gridsuite/commons-ui'; -import { mergeSx } from '../../../utils/functions'; +import { mergeSx, useDebounce } from '@gridsuite/commons-ui'; import { arrayFrom } from '../../../utils/utils'; const styles = { diff --git a/src/components/results/dynamicsimulation/timeseries/dynamic-simulation-result-series-chart.jsx b/src/components/results/dynamicsimulation/timeseries/dynamic-simulation-result-series-chart.jsx index 7aede57f1e..2a6c5959e2 100644 --- a/src/components/results/dynamicsimulation/timeseries/dynamic-simulation-result-series-chart.jsx +++ b/src/components/results/dynamicsimulation/timeseries/dynamic-simulation-result-series-chart.jsx @@ -16,7 +16,7 @@ import TooltipIconButton from '../common/tooltip-icon-button'; import { lighten } from '@mui/material/styles'; import { useIntl } from 'react-intl'; import { SeriesType } from '../plot/plot-types'; -import { mergeSx } from '../../../utils/functions'; +import { mergeSx } from '@gridsuite/commons-ui'; const styles = { plotScaleButton: (theme) => ({ diff --git a/src/components/results/loadflow/load-flow-result-tab.tsx b/src/components/results/loadflow/load-flow-result-tab.tsx index 07b8a54932..857f5e008e 100644 --- a/src/components/results/loadflow/load-flow-result-tab.tsx +++ b/src/components/results/loadflow/load-flow-result-tab.tsx @@ -34,11 +34,10 @@ import { FILTER_DATA_TYPES, FILTER_TEXT_COMPARATORS } from 'components/custom-ag import { LimitViolationResult } from './limit-violation-result'; import { NumberCellRenderer, StatusCellRender } from '../common/result-cell-renderers'; import ResultsGlobalFilter, { Filter, FilterType } from '../common/results-global-filter'; -import { useSnackMessage } from '@gridsuite/commons-ui'; +import { mergeSx, useSnackMessage } from '@gridsuite/commons-ui'; import { fetchAllCountries, fetchAllNominalVoltages } from '../../../services/study/network-map'; import { LOADFLOW_RESULT_SORT_STORE } from 'utils/store-sort-filter-fields'; import GlassPane from '../common/glass-pane'; -import { mergeSx } from '../../utils/functions'; import { FilterType as AgGridFilterType } from '../../../types/custom-aggrid-types'; import { useFilterSelector } from '../../../hooks/use-filter-selector'; import { mapFieldsToColumnsFilter } from '../../../utils/aggrid-headers-utils'; diff --git a/src/components/results/securityanalysis/security-analysis.type.ts b/src/components/results/securityanalysis/security-analysis.type.ts index c6d3dd5bd2..92b1316cd6 100644 --- a/src/components/results/securityanalysis/security-analysis.type.ts +++ b/src/components/results/securityanalysis/security-analysis.type.ts @@ -10,6 +10,7 @@ import { ColDef, ICellRendererParams } from 'ag-grid-community'; import { AgGridReactProps } from 'ag-grid-react'; import { UUID } from 'crypto'; import { FilterConfig, SortConfig } from '../../../types/custom-aggrid-types'; +import { TablePaginationProps } from '@mui/material'; export interface LimitViolation { subjectId?: string; @@ -93,14 +94,6 @@ type Pageable = { unpaged?: boolean; }; -type PaginationProps = { - count?: number; - rowsPerPage?: number; - page?: number; - onPageChange?: (event: React.MouseEvent | null, page: number) => void; - onRowsPerPageChange?: React.ChangeEventHandler; -}; - export type SubjectIdRendererType = (cellData: ICellRendererParams) => React.JSX.Element | undefined; export interface SecurityAnalysisNmkResult { @@ -136,7 +129,7 @@ export interface SecurityAnalysisResultNmkProps { columnDefs: ColDef[]; isLoadingResult: boolean; isFromContingency: boolean; - paginationProps: PaginationProps; + paginationProps: TablePaginationProps; } export interface SecurityAnalysisNTableRow { diff --git a/src/components/spreadsheet/table-wrapper.tsx b/src/components/spreadsheet/table-wrapper.tsx index 67fe3e35a0..d8f4ba4e36 100644 --- a/src/components/spreadsheet/table-wrapper.tsx +++ b/src/components/spreadsheet/table-wrapper.tsx @@ -12,7 +12,7 @@ import { FormattedMessage } from 'react-intl'; import { Alert, Box, Grid } from '@mui/material'; import { Theme } from '@mui/material/styles'; import { EquipmentTable } from './equipment-table'; -import { Identifiable, useSnackMessage } from '@gridsuite/commons-ui'; +import { Identifiable, mergeSx, useSnackMessage } from '@gridsuite/commons-ui'; import { PARAM_DEVELOPER_MODE } from '../../utils/config-params'; import { ColumnsConfig } from './columns-config'; import { EquipmentTabs } from './equipment-tabs'; @@ -24,7 +24,6 @@ import CustomColumnsConfig from './custom-columns/custom-columns-config'; import { AppState, CurrentTreeNode } from '../../redux/reducer'; import { AgGridReact } from 'ag-grid-react'; import { ColumnMovedEvent, ColumnState, RowClickedEvent } from 'ag-grid-community'; -import { mergeSx } from '../utils/functions'; import { CustomColDef } from '../custom-aggrid/custom-aggrid-header.type'; import { SpreadsheetEquipmentType } from './config/spreadsheet.type'; import SpreadsheetSave from './spreadsheet-save'; diff --git a/src/components/spreadsheet/utils/cell-renderers.tsx b/src/components/spreadsheet/utils/cell-renderers.tsx index c49254d23a..28f7c79595 100644 --- a/src/components/spreadsheet/utils/cell-renderers.tsx +++ b/src/components/spreadsheet/utils/cell-renderers.tsx @@ -9,11 +9,11 @@ import { Box, Checkbox, Tooltip } from '@mui/material'; import { Theme } from '@mui/material/styles'; import { ReactNode, useEffect, useRef, useState } from 'react'; -import { mergeSx } from '../../utils/functions'; import { isBlankOrEmpty } from 'components/utils/validation-functions'; import { IntlShape } from 'react-intl'; import { ICellRendererParams } from 'ag-grid-community'; import { CustomCellRendererProps } from 'ag-grid-react'; +import { mergeSx } from '@gridsuite/commons-ui'; const styles = { tableCell: (theme: Theme) => ({ diff --git a/src/components/study-container.jsx b/src/components/study-container.jsx index c7690dc42a..85c57adb1d 100644 --- a/src/components/study-container.jsx +++ b/src/components/study-container.jsx @@ -25,7 +25,7 @@ import { import { fetchRootNetworks } from 'services/root-network'; import WaitingLoader from './utils/waiting-loader'; -import { fetchDirectoryElementPath, useIntlRef, useSnackMessage } from '@gridsuite/commons-ui'; +import { fetchDirectoryElementPath, useIntlRef, usePrevious, useSnackMessage } from '@gridsuite/commons-ui'; import NetworkModificationTreeModel from './graph/network-modification-tree-model'; import { getFirstNodeOfType, isNodeBuilt, isNodeRenamed, isSameNode } from './graph/util/model-functions'; import { RunningStatus } from './utils/running-status'; @@ -44,7 +44,6 @@ import { fetchNetworkExistence, fetchStudyIndexationStatus } from '../services/s import { recreateStudyNetwork, reindexAllStudy } from 'services/study/study'; import { HttpStatusCode } from 'utils/http-status-code'; -import { usePrevious } from './utils/utils'; import { StudyIndexationStatus } from 'redux/reducer'; import { NodeType } from './graph/tree-node.type'; diff --git a/src/components/tooltips/equipment-popover.jsx b/src/components/tooltips/equipment-popover.jsx index bb1f7809e7..135af39f3a 100644 --- a/src/components/tooltips/equipment-popover.jsx +++ b/src/components/tooltips/equipment-popover.jsx @@ -15,8 +15,7 @@ import { useSelector } from 'react-redux'; import { RunningStatus } from '../utils/running-status'; import { EQUIPMENT_INFOS_TYPES, EQUIPMENT_TYPES } from 'components/utils/equipment-types'; import { fetchNetworkElementInfos } from '../../services/study/network'; -import { mergeSx } from '../utils/functions'; -import { convertInputValue, FieldType, useDebounce } from '@gridsuite/commons-ui'; +import { convertInputValue, FieldType, mergeSx, useDebounce } from '@gridsuite/commons-ui'; const styles = { tableCells: { diff --git a/src/components/utils/alert-custom-message-node.jsx b/src/components/utils/alert-custom-message-node.tsx similarity index 58% rename from src/components/utils/alert-custom-message-node.jsx rename to src/components/utils/alert-custom-message-node.tsx index 4cb2cc8454..b91f7ab534 100644 --- a/src/components/utils/alert-custom-message-node.jsx +++ b/src/components/utils/alert-custom-message-node.tsx @@ -5,10 +5,9 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +import { mergeSx } from '@gridsuite/commons-ui'; import Alert from '@mui/material/Alert'; import { FormattedMessage } from 'react-intl'; -import PropTypes from 'prop-types'; -import { mergeSx } from './functions'; const styles = { customMessageNode: { @@ -18,24 +17,19 @@ const styles = { }, }; -const AlertCustomMessageNode = (props) => { - const { noMargin, message } = props; +interface AlertCustomMessageNodeProps { + message: string; + noMargin?: boolean; +} + +const AlertCustomMessageNode = (props: AlertCustomMessageNodeProps) => { + const { noMargin = false, message } = props; return ( - + ); }; -AlertCustomMessageNode.defaultProps = { - noMargin: false, - message: '', -}; - -AlertCustomMessageNode.propTypes = { - noMargin: PropTypes.bool, - message: PropTypes.string, -}; - export default AlertCustomMessageNode; diff --git a/src/components/utils/ask-text-dialog.tsx b/src/components/utils/ask-text-dialog.tsx index 153fc90f52..2bb15b73bc 100644 --- a/src/components/utils/ask-text-dialog.tsx +++ b/src/components/utils/ask-text-dialog.tsx @@ -9,11 +9,11 @@ import { useIntl } from 'react-intl'; import { Dialog, DialogActions, DialogContent, DialogTitle } from '@mui/material'; import Button from '@mui/material/Button'; import { FunctionComponent, useEffect, useState } from 'react'; -import { useValidNodeName } from './inputs/input-hooks'; import { useSelector } from 'react-redux'; import Alert from '@mui/material/Alert'; import { CancelButton } from '@gridsuite/commons-ui'; import { AppState } from '../../redux/reducer'; +import { useValidNodeName } from './inputs/input-hooks'; interface AskTextDialogProps { title: string; diff --git a/src/components/utils/custom-nested-menu.jsx b/src/components/utils/custom-nested-menu.tsx similarity index 67% rename from src/components/utils/custom-nested-menu.jsx rename to src/components/utils/custom-nested-menu.tsx index ad03574d71..316a1a769f 100644 --- a/src/components/utils/custom-nested-menu.jsx +++ b/src/components/utils/custom-nested-menu.tsx @@ -4,10 +4,10 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -import { useState } from 'react'; -import { NestedMenuItem } from 'mui-nested-menu'; -import { mergeSx } from './functions'; -import { Box, MenuItem } from '@mui/material'; +import { ForwardRefExoticComponent, PropsWithChildren, RefAttributes, useState } from 'react'; +import { NestedMenuItem, NestedMenuItemProps } from 'mui-nested-menu'; +import { Box, ExtendButtonBase, MenuItem, MenuItemProps, MenuItemTypeMap, SxProps } from '@mui/material'; +import { mergeSx } from '@gridsuite/commons-ui'; const styles = { highlightedParentLine: { @@ -24,15 +24,19 @@ const styles = { }, }; -export const CustomNestedMenuItem = (props) => { +interface CustomNestedMenuItemProps extends PropsWithChildren, Omit { + sx?: SxProps; +} + +export const CustomNestedMenuItem = (props: CustomNestedMenuItemProps) => { const { sx, children, ...other } = props; const [isSubMenuActive, setSubMenuActive] = useState(false); return ( setSubMenuActive(true)} onMouseLeave={() => setSubMenuActive(false)}> {children} @@ -41,7 +45,7 @@ export const CustomNestedMenuItem = (props) => { ); }; -export const CustomMenuItem = (props) => { +export const CustomMenuItem = (props: MenuItemProps) => { const { sx, ...other } = props; return ; diff --git a/src/components/utils/custom-table-pagination.jsx b/src/components/utils/custom-table-pagination.tsx similarity index 87% rename from src/components/utils/custom-table-pagination.jsx rename to src/components/utils/custom-table-pagination.tsx index 7ebd5212ae..8f110b21dc 100644 --- a/src/components/utils/custom-table-pagination.jsx +++ b/src/components/utils/custom-table-pagination.tsx @@ -7,13 +7,17 @@ import { useIntl } from 'react-intl'; import PropTypes from 'prop-types'; -import { TablePagination } from '@mui/material'; +import { LabelDisplayedRowsArgs, TablePagination, TablePaginationProps } from '@mui/material'; -const CustomTablePagination = (props) => { +type CustomTablePaginationProps = TablePaginationProps & { + labelRowsPerPageId?: string; +}; + +const CustomTablePagination = (props: CustomTablePaginationProps) => { const { labelRowsPerPageId, ...otherProps } = props; const intl = useIntl(); - const customLabelDisplayedRows = ({ from, to, count }) => { + const customLabelDisplayedRows = ({ from, to, count }: LabelDisplayedRowsArgs) => { return `${from}-${to} ${intl.formatMessage({ id: 'muiTablePaginationOfLabel', })} ${count}`; @@ -61,14 +65,11 @@ const CustomTablePagination = (props) => { } }} {...otherProps} - > - {props.children} - + /> ); }; CustomTablePagination.propTypes = { - children: PropTypes.node, rowsPerPageOptions: PropTypes.arrayOf( PropTypes.oneOfType([ PropTypes.number, diff --git a/src/components/utils/equipmentInfosHandler.js b/src/components/utils/equipmentInfosHandler.ts similarity index 76% rename from src/components/utils/equipmentInfosHandler.js rename to src/components/utils/equipmentInfosHandler.ts index 3297fd1cbd..fe54e6b5fa 100644 --- a/src/components/utils/equipmentInfosHandler.js +++ b/src/components/utils/equipmentInfosHandler.ts @@ -8,16 +8,18 @@ import { useCallback } from 'react'; import { useSelector } from 'react-redux'; import { PARAM_USE_NAME } from '../../utils/config-params'; +import { AppState } from 'redux/reducer'; +import { Identifiable } from '@gridsuite/commons-ui'; export const useNameOrId = () => { - const useName = useSelector((state) => state[PARAM_USE_NAME]); + const useName = useSelector((state: AppState) => state[PARAM_USE_NAME]); const getNameOrId = useCallback( - (infos) => { - if (infos != null) { + (infos?: Identifiable) => { + if (infos) { const name = infos.name; return useName && name != null && name.trim() !== '' ? name : infos?.id; } - return null; + return ''; }, [useName] ); diff --git a/src/components/utils/functions.js b/src/components/utils/functions.js deleted file mode 100644 index d3df2a1198..0000000000 --- a/src/components/utils/functions.js +++ /dev/null @@ -1,8 +0,0 @@ -/** - * Copyright (c) 2023, RTE (http://www.rte-france.com) - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ - -export const mergeSx = (...allSx) => allSx.flat(); diff --git a/src/components/utils/inputs/input-hooks.jsx b/src/components/utils/inputs/input-hooks.jsx deleted file mode 100644 index a375155590..0000000000 --- a/src/components/utils/inputs/input-hooks.jsx +++ /dev/null @@ -1,384 +0,0 @@ -/** - * Copyright (c) 2022, RTE (http://www.rte-france.com) - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ - -import InputAdornment from '@mui/material/InputAdornment'; -import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; -import { FormattedMessage, useIntl } from 'react-intl'; -import { CircularProgress, InputLabel, TextField, Tooltip, Button, Grid } from '@mui/material'; -import CheckIcon from '@mui/icons-material/Check'; -import FolderIcon from '@mui/icons-material/Folder'; -import FormControl from '@mui/material/FormControl'; -import IconButton from '@mui/material/IconButton'; - -import { styles } from '../../dialogs/dialog-utils'; -import { useSnackMessage, OverflowableText, useDebounce, FieldLabel } from '@gridsuite/commons-ui'; -import { TOOLTIP_DELAY } from '../../../utils/UIconstants'; -import Chip from '@mui/material/Chip'; -import { DirectoryItemSelector } from '@gridsuite/commons-ui'; -import { useCSVReader } from 'react-papaparse'; -import { isNodeExists } from '../../../services/study'; -import { mergeSx } from '../functions'; - -export const useInputForm = () => { - const validationMap = useRef(new Map()); - const [toggleClear, setToggleClear] = useState(false); - const [hasChanged, setHasChanged] = useState(false); - const validate = useCallback(() => { - // Check if error list contains an error - return Array.from(validationMap.current.values()) - .map((e) => e()) - .every((res) => res); - }, []); - - const addValidation = useCallback((id, validate) => { - validationMap.current.set(id, validate); - }, []); - - const removeValidation = useCallback((id) => { - validationMap.current.delete(id); - }, []); - - const clear = useCallback(() => { - setToggleClear((oldValue) => !oldValue); - }, []); - const reset = useCallback((label, validate) => { - validationMap.current = new Map(); - }, []); - - return useMemo(() => { - return { - toggleClear, - clear, - validate, - addValidation, - reset, - hasChanged, - setHasChanged, - removeValidation, - }; - }, [toggleClear, clear, validate, addValidation, reset, hasChanged, removeValidation]); -}; - -export const useButtonWithTooltip = ({ handleClick, label, icon }) => { - return useMemo(() => { - return ( - } - placement="top" - arrow - enterDelay={TOOLTIP_DELAY} - enterNextDelay={TOOLTIP_DELAY} - slotProps={{ - popper: { - sx: { - '& .MuiTooltip-tooltip': styles.tooltip, - }, - }, - }} - > - - {icon} - - - ); - }, [label, handleClick, icon]); -}; - -export const useSimpleTextValue = ({ defaultValue, adornment, error, triggerReset }) => { - const [value, setValue] = useState(defaultValue); - - const handleChangeValue = useCallback((event) => { - setValue(event.target.value); - }, []); - - const field = useMemo(() => { - return ( - - ); - }, [value, handleChangeValue, adornment, error]); - - useEffect(() => setValue(defaultValue), [defaultValue, triggerReset]); - - return [value, field]; -}; - -const inputAdornment = (content) => { - return { - endAdornment: {content}, - }; -}; - -export const useValidNodeName = ({ studyUuid, defaultValue, triggerReset }) => { - const intl = useIntl(); - const { snackError } = useSnackMessage(); - const [isValidName, setIsValidName] = useState(false); - const [error, setError] = useState(); - const [checking, setChecking] = useState(undefined); - const [adornment, setAdornment] = useState(); - const [name, field] = useSimpleTextValue({ - defaultValue, - adornment, - error, - triggerReset, - }); - - const validName = useCallback( - (name) => { - if (name !== defaultValue) { - isNodeExists(studyUuid, name) - .then((response) => { - if (response.status === 200) { - setError( - intl.formatMessage({ - id: 'nodeNameAlreadyUsed', - }) - ); - setIsValidName(false); - } else { - setIsValidName(true); - } - setChecking(false); - }) - .catch((error) => { - snackError({ - messageTxt: error.message, - headerId: 'NodeUpdateError', - }); - }); - } else { - setChecking(undefined); - } - }, - [studyUuid, intl, defaultValue, snackError] - ); - const debouncedValidName = useDebounce(validName, 700); - - useEffect(() => { - if (checking === undefined) { - setAdornment(null); - } - if (checking) { - setAdornment(inputAdornment()); - } else if (!isValidName) { - setAdornment(undefined); - } else { - setAdornment(inputAdornment()); - } - }, [checking, isValidName]); - - useEffect(() => { - if (name === '') { - return; - } // initial render - - setIsValidName(false); - setAdornment(undefined); - setChecking(true); - setError(undefined); - debouncedValidName(name); - }, [studyUuid, name, debouncedValidName, triggerReset]); - - return [error, field, isValidName, name]; -}; - -export const useDirectoryElements = ({ - label, - initialValues, - elementType, - equipmentTypes, - titleId, - elementStyle, - itemFilter = undefined, - errorMsg = undefined, - inputForm = undefined, -}) => { - const [values, setValues] = useState(initialValues); - const [directoryItemSelectorOpen, setDirectoryItemSelectorOpen] = useState(false); - const intl = useIntl(); - const { snackError } = useSnackMessage(); - const refInitialValues = useRef(); - refInitialValues.current = initialValues; - const types = useMemo(() => [elementType], [elementType]); - - useEffect(() => { - if (refInitialValues.current) { - setValues(refInitialValues.current); - } - }, []); - - const handleDelete = useCallback( - (item, index) => { - let arr = [...values]; - arr.splice(index, 1); - inputForm?.setHasChanged(arr.length > 0); - setValues(arr); - }, - [inputForm, values] - ); - - const addElements = useCallback( - (elements) => { - let elementsToAdd = []; - elements.forEach((element) => { - const { icon, children, ...elementRest } = element; - // check if element is already present - if (values.find((v) => v.id === elementRest.id) !== undefined) { - snackError({ - messageTxt: '', - headerId: 'directory_items_input/ElementAlreadyUsed', - }); - } else { - elementsToAdd.push(elementRest); - } - }); - if (elementsToAdd.length > 0) { - inputForm?.setHasChanged(true); - setValues(values.concat(elementsToAdd)); - } - - setDirectoryItemSelectorOpen(false); - }, - [values, snackError, inputForm] - ); - - const field = useMemo(() => { - return ( - <> - - {values?.length === 0 && ( - - - - - - - - )} - {values?.length > 0 && ( - -
- {values.map((item, index) => ( - handleDelete(item, index)} - label={} - /> - ))} -
-
- )} - - - setDirectoryItemSelectorOpen(true)} - > - - - - -
- - - ); - }, [ - errorMsg, - values, - label, - directoryItemSelectorOpen, - addElements, - equipmentTypes, - intl, - titleId, - itemFilter, - elementStyle, - handleDelete, - types, - ]); - return [values, field]; -}; - -export const useCSVPicker = ({ label, header, resetTrigger, maxTapNumber, disabled = false }) => { - const intl = useIntl(); - - const { CSVReader } = useCSVReader(); - const [_acceptedFile, setAcceptedFile] = useState(); - const [fileError, setFileError] = useState(); - - const equals = (a, b) => b.every((item) => a.includes(item)); - useEffect(() => { - setAcceptedFile(); - }, [resetTrigger]); - - const field = useMemo(() => { - return ( - <> - { - setAcceptedFile(acceptedFile); - if (results?.data.length > 0 && equals(header, results.data[0])) { - setFileError(); - } else { - setFileError( - intl.formatMessage({ - id: 'InvalidRuleHeader', - }) - ); - } - - if (results.data.length > maxTapNumber) { - setFileError(intl.formatMessage({ id: 'TapPositionValueError' }, { value: maxTapNumber })); - } - }} - > - {({ getRootProps }) => ( - - - - {_acceptedFile - ? _acceptedFile.name - : intl.formatMessage({ - id: 'uploadMessage', - })} - - - )} - - - ); - }, [_acceptedFile, disabled, header, intl, label, maxTapNumber]); - - return [_acceptedFile, field, fileError]; -}; diff --git a/src/components/utils/inputs/input-hooks.tsx b/src/components/utils/inputs/input-hooks.tsx new file mode 100644 index 0000000000..58e8773530 --- /dev/null +++ b/src/components/utils/inputs/input-hooks.tsx @@ -0,0 +1,239 @@ +/** + * Copyright (c) 2022, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +import InputAdornment from '@mui/material/InputAdornment'; +import React, { ReactNode, useCallback, useEffect, useMemo, useState } from 'react'; +import { FormattedMessage, useIntl } from 'react-intl'; +import { CircularProgress, TextField, Tooltip, Button, Grid, TextFieldProps } from '@mui/material'; +import CheckIcon from '@mui/icons-material/Check'; +import IconButton from '@mui/material/IconButton'; + +import { styles } from '../../dialogs/dialog-utils'; +import { useSnackMessage, useDebounce } from '@gridsuite/commons-ui'; +import { TOOLTIP_DELAY } from '../../../utils/UIconstants'; +import { useCSVReader } from 'react-papaparse'; +import { isNodeExists } from '../../../services/study'; +import { UUID } from 'crypto'; + +interface UseButtonWithTooltipProps { + handleClick: React.MouseEventHandler; + label: string; + icon: ReactNode; +} + +export const useButtonWithTooltip = ({ handleClick, label, icon }: UseButtonWithTooltipProps) => { + return useMemo(() => { + return ( + } + placement="top" + arrow + enterDelay={TOOLTIP_DELAY} + enterNextDelay={TOOLTIP_DELAY} + slotProps={{ + popper: { + sx: { + '& .MuiTooltip-tooltip': styles.tooltip, + }, + }, + }} + > + + {icon} + + + ); + }, [label, handleClick, icon]); +}; + +interface UseSimpleTextValueProps { + defaultValue: string; + adornment: TextFieldProps['InputProps']; + error: boolean; + triggerReset: boolean; +} + +export const useSimpleTextValue = ({ defaultValue, adornment, error, triggerReset }: UseSimpleTextValueProps) => { + const [value, setValue] = useState(defaultValue); + + const handleChangeValue = useCallback((event: React.ChangeEvent) => { + setValue(event.target.value); + }, []); + + const field = useMemo(() => { + return ( + + ); + }, [value, handleChangeValue, adornment, error]); + + useEffect(() => setValue(defaultValue), [defaultValue, triggerReset]); + + return [value, field] as const; +}; + +const inputAdornment = (content: ReactNode) => { + return { + endAdornment: {content}, + }; +}; + +interface UseValidNodeName { + studyUuid: UUID | null; + defaultValue: string; + triggerReset: boolean; +} + +export const useValidNodeName = ({ studyUuid, defaultValue, triggerReset }: UseValidNodeName) => { + const intl = useIntl(); + const { snackError } = useSnackMessage(); + const [isValidName, setIsValidName] = useState(false); + const [error, setError] = useState(); + const [checking, setChecking] = useState(undefined); + const [adornment, setAdornment] = useState(); + const [name, field] = useSimpleTextValue({ + defaultValue, + adornment, + error: !!error, + triggerReset, + }); + + const validName = useCallback( + (name: string) => { + if (!studyUuid) { + return; + } + if (name !== defaultValue) { + isNodeExists(studyUuid, name) + .then((response) => { + if (response.status === 200) { + setError( + intl.formatMessage({ + id: 'nodeNameAlreadyUsed', + }) + ); + setIsValidName(false); + } else { + setIsValidName(true); + } + setChecking(false); + }) + .catch((error) => { + snackError({ + messageTxt: error.message, + headerId: 'NodeUpdateError', + }); + }); + } else { + setChecking(undefined); + } + }, + [studyUuid, intl, defaultValue, snackError] + ); + const debouncedValidName = useDebounce(validName, 700); + + useEffect(() => { + if (checking === undefined) { + setAdornment(undefined); + } + if (checking) { + setAdornment(inputAdornment()); + } else if (!isValidName) { + setAdornment(undefined); + } else { + setAdornment(inputAdornment()); + } + }, [checking, isValidName]); + + useEffect(() => { + if (name === '') { + return; + } // initial render + + setIsValidName(false); + setAdornment(undefined); + setChecking(true); + setError(undefined); + debouncedValidName(name); + }, [studyUuid, name, debouncedValidName, triggerReset]); + + return [error, field, isValidName, name] as const; +}; + +interface UseCSVPickerProps { + label: string; + header: string[]; + resetTrigger: boolean; + maxTapNumber?: number; + disabled?: boolean; +} + +export const useCSVPicker = ({ label, header, resetTrigger, maxTapNumber, disabled = false }: UseCSVPickerProps) => { + const intl = useIntl(); + + const { CSVReader } = useCSVReader(); + const [_acceptedFile, setAcceptedFile] = useState(); + const [fileError, setFileError] = useState(); + + const equals = (a: string[], b: string[]) => b.every((item) => a.includes(item)); + useEffect(() => { + setAcceptedFile(undefined); + }, [resetTrigger]); + + const field = useMemo(() => { + return ( + <> + { + setAcceptedFile(acceptedFile); + if (results?.data.length > 0 && equals(header, results.data[0])) { + setFileError(undefined); + } else { + setFileError( + intl.formatMessage({ + id: 'InvalidRuleHeader', + }) + ); + } + + if (maxTapNumber && results.data.length > maxTapNumber) { + setFileError(intl.formatMessage({ id: 'TapPositionValueError' }, { value: maxTapNumber })); + } + }} + > + {({ getRootProps }: { getRootProps: () => any }) => ( + + + + {_acceptedFile + ? _acceptedFile.name + : intl.formatMessage({ + id: 'uploadMessage', + })} + + + )} + + + ); + }, [_acceptedFile, disabled, header, intl, label, maxTapNumber]); + + return [_acceptedFile, field, fileError] as const; +}; diff --git a/src/components/utils/is-any-node-building-hook.js b/src/components/utils/is-any-node-building-hook.ts similarity index 72% rename from src/components/utils/is-any-node-building-hook.js rename to src/components/utils/is-any-node-building-hook.ts index d56e0f2a53..b6469750ef 100644 --- a/src/components/utils/is-any-node-building-hook.js +++ b/src/components/utils/is-any-node-building-hook.ts @@ -7,14 +7,15 @@ import { useEffect, useState } from 'react'; import { useSelector } from 'react-redux'; +import { AppState } from 'redux/reducer'; export const useIsAnyNodeBuilding = () => { const [iAnyNodeBuild, setAnyNodeBuilding] = useState(false); - const treeModel = useSelector((state) => state.networkModificationTreeModel); + const treeModel = useSelector((state: AppState) => state.networkModificationTreeModel); useEffect(() => { - setAnyNodeBuilding(treeModel.isAnyNodeBuilding); + setAnyNodeBuilding(treeModel?.isAnyNodeBuilding || false); }, [treeModel]); return iAnyNodeBuild; diff --git a/src/components/utils/list-item-with-delete-button.jsx b/src/components/utils/list-item-with-delete-button.jsx deleted file mode 100644 index fe0066a68a..0000000000 --- a/src/components/utils/list-item-with-delete-button.jsx +++ /dev/null @@ -1,68 +0,0 @@ -/** - * Copyright (c) 2022, RTE (http://www.rte-france.com) - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ - -import { useState } from 'react'; -import ListItem from '@mui/material/ListItem'; -import ListItemIcon from '@mui/material/ListItemIcon'; -import Checkbox from '@mui/material/Checkbox'; -import ListItemText from '@mui/material/ListItemText'; -import IconButton from '@mui/material/IconButton'; -import DeleteIcon from '@mui/icons-material/Delete'; -import * as PropTypes from 'prop-types'; -import { ListItemButton } from '@mui/material'; - -const ListItemWithDeleteButton = (props) => { - const [isHoover, setHoover] = useState(false); - - return ( - setHoover(true)} - onMouseLeave={() => setHoover(false)} - disablePadding - secondaryAction={ - props.removeFromList && - isHoover && ( - - - - ) - } - > - - - - - - - - ); -}; - -ListItemWithDeleteButton.propTypes = { - onClick: PropTypes.func, - set: PropTypes.any, - value: PropTypes.any, - primary: PropTypes.any, - removeFromList: PropTypes.any, -}; - -export default ListItemWithDeleteButton; diff --git a/src/components/utils/loader-with-overlay.jsx b/src/components/utils/loader-with-overlay.tsx similarity index 76% rename from src/components/utils/loader-with-overlay.jsx rename to src/components/utils/loader-with-overlay.tsx index 53e0a06d15..6e43807c6b 100644 --- a/src/components/utils/loader-with-overlay.jsx +++ b/src/components/utils/loader-with-overlay.tsx @@ -5,7 +5,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -import { Box, CircularProgress } from '@mui/material'; +import { Box, CircularProgress, CircularProgressProps } from '@mui/material'; import { FormattedMessage } from 'react-intl'; const styles = { @@ -26,7 +26,14 @@ const styles = { }, }; -const LoaderWithOverlay = ({ color, loaderSize, loadingMessageText, isFixed }) => { +interface LoaderWithOverlayProps { + color: CircularProgressProps['color']; + loaderSize: CircularProgressProps['size']; + loadingMessageText: string; + isFixed?: boolean; +} + +const LoaderWithOverlay = ({ color, loaderSize, loadingMessageText, isFixed }: LoaderWithOverlayProps) => { return ( diff --git a/src/components/utils/localized-countries-hook.js b/src/components/utils/localized-countries-hook.ts similarity index 83% rename from src/components/utils/localized-countries-hook.js rename to src/components/utils/localized-countries-hook.ts index 05b2010fbd..11bcc6c917 100644 --- a/src/components/utils/localized-countries-hook.js +++ b/src/components/utils/localized-countries-hook.ts @@ -10,17 +10,17 @@ import { PARAM_LANGUAGE } from '../../utils/config-params'; import { useCallback, useEffect, useMemo, useState } from 'react'; import { getComputedLanguage } from '../../utils/language'; import localizedCountries from 'localized-countries'; -import countriesFr from 'localized-countries/data/fr'; -import countriesEn from 'localized-countries/data/en'; +import countriesFr from 'localized-countries/data/fr.json'; +import countriesEn from 'localized-countries/data/en.json'; export const useLocalizedCountries = () => { const [languageLocal] = useParameterState(PARAM_LANGUAGE); - const [localizedCountriesModule, setLocalizedCountriesModule] = useState(); + const [localizedCountriesModule, setLocalizedCountriesModule] = useState(); //TODO FM this is disgusting, can we make it better ? useEffect(() => { const lang = getComputedLanguage(languageLocal).substr(0, 2); - let localizedCountriesResult; + let localizedCountriesResult: localizedCountries.LocalizedCountries; // vite does not support ESM dynamic imports on node_modules, so we have to imports the languages before and do this // https://github.com/vitejs/vite/issues/14102 if (lang === 'fr') { @@ -40,7 +40,7 @@ export const useLocalizedCountries = () => { ); const translate = useCallback( - (countryCode) => (localizedCountriesModule ? localizedCountriesModule.get(countryCode) : ''), + (countryCode: string) => (localizedCountriesModule ? localizedCountriesModule.get(countryCode) : ''), [localizedCountriesModule] ); diff --git a/src/components/utils/remote-resource-handler.js b/src/components/utils/remote-resource-handler.js deleted file mode 100644 index 81ae659032..0000000000 --- a/src/components/utils/remote-resource-handler.js +++ /dev/null @@ -1,42 +0,0 @@ -/** - * Copyright (c) 2021, RTE (http://www.rte-france.com) - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ - -// TODO to be replaced with a custom hook -export class RemoteResourceHandler { - fetcher = undefined; - errorHandler = undefined; - postUpdate = undefined; - - constructor(fetcher, setter, errorHandler) { - this.fetcher = fetcher; - this.setter = setter; - this.errorHandler = errorHandler; - } - - fetched = undefined; - - fetch() { - if (this.fetched === undefined) { - this.fetched = false; - this.fetcher() - .then((val) => { - this.fetched = true; - this.setter(val); - }) - .catch((error) => { - if (this.errorHandler) { - this.errorHandler(error); - } - }); - } - return this.fetched; - } - - isFetched() { - return this.fetched === true; - } -} diff --git a/src/components/utils/rhf-inputs/boolean-nullable-input.jsx b/src/components/utils/rhf-inputs/boolean-nullable-input.tsx similarity index 70% rename from src/components/utils/rhf-inputs/boolean-nullable-input.jsx rename to src/components/utils/rhf-inputs/boolean-nullable-input.tsx index be552ae838..2df4f60016 100644 --- a/src/components/utils/rhf-inputs/boolean-nullable-input.jsx +++ b/src/components/utils/rhf-inputs/boolean-nullable-input.tsx @@ -6,30 +6,35 @@ */ import FormControl from '@mui/material/FormControl'; -import { Checkbox, FormControlLabel, FormHelperText } from '@mui/material'; +import { Checkbox, CheckboxProps, FormControlLabel, FormHelperText } from '@mui/material'; import { useIntl } from 'react-intl'; import { useController } from 'react-hook-form'; import { useCallback } from 'react'; -const CheckboxNullableInput = ({ name, label, id, formProps, previousValue }) => { +interface CheckboxNullableInputProps { + name: string; + label: string; + id?: string; + formProps?: CheckboxProps; + previousValue?: string; +} + +const CheckboxNullableInput = ({ name, label, id, formProps, previousValue }: CheckboxNullableInputProps) => { const { field: { onChange, value }, } = useController({ name }); const intl = useIntl(); - const handleChangeValue = useCallback( - (event) => { - if (value) { - onChange(null); - } else if (value === null) { - onChange(false); - } else { - onChange(true); - } - }, - [onChange, value] - ); + const handleChangeValue = useCallback(() => { + if (value) { + onChange(null); + } else if (value === null) { + onChange(false); + } else { + onChange(true); + } + }, [onChange, value]); return ( @@ -39,7 +44,7 @@ const CheckboxNullableInput = ({ name, label, id, formProps, previousValue }) => handleChangeValue(e)} + onChange={handleChangeValue} value="checked" inputProps={{ 'aria-label': 'primary checkbox', diff --git a/src/components/utils/rhf-inputs/chip-items-input.jsx b/src/components/utils/rhf-inputs/chip-items-input.tsx similarity index 90% rename from src/components/utils/rhf-inputs/chip-items-input.jsx rename to src/components/utils/rhf-inputs/chip-items-input.tsx index 42606f2814..f52e09e2f8 100644 --- a/src/components/utils/rhf-inputs/chip-items-input.jsx +++ b/src/components/utils/rhf-inputs/chip-items-input.tsx @@ -21,7 +21,13 @@ import { useCallback, useState } from 'react'; import { useController, useFieldArray } from 'react-hook-form'; import { isFieldRequired } from '../utils'; -const ChipItemsInput = ({ label, name, hideErrorMessage }) => { +interface ChipItemsInputProps { + label: string; + name: string; + hideErrorMessage: boolean; +} + +const ChipItemsInput = ({ label, name, hideErrorMessage }: ChipItemsInputProps) => { const [textEntered, setTextEntered] = useState(''); const { snackError } = useSnackMessage(); @@ -42,9 +48,9 @@ const ChipItemsInput = ({ label, name, hideErrorMessage }) => { }); const addItem = useCallback( - (value) => { + (value: string) => { // check if element is already present - if (getValues(name).find((v) => v === value) !== undefined) { + if (getValues(name).find((v: string) => v === value) !== undefined) { snackError({ messageTxt: '', headerId: 'directory_items_input/ElementAlreadyUsed', @@ -56,7 +62,7 @@ const ChipItemsInput = ({ label, name, hideErrorMessage }) => { [append, getValues, snackError, name] ); - const keyPress = (e) => { + const keyPress = (e: React.KeyboardEvent) => { if (e.keyCode === 13 && textEntered.length > 0) { addItem(textEntered); setTextEntered(''); @@ -70,7 +76,7 @@ const ChipItemsInput = ({ label, name, hideErrorMessage }) => { } }; - const handleChange = (e) => { + const handleChange = (e: React.ChangeEvent) => { setTextEntered(e.target.value); }; diff --git a/src/components/utils/rhf-inputs/country-selection-input.jsx b/src/components/utils/rhf-inputs/country-selection-input.tsx similarity index 60% rename from src/components/utils/rhf-inputs/country-selection-input.jsx rename to src/components/utils/rhf-inputs/country-selection-input.tsx index 38ac091559..5955dacdde 100644 --- a/src/components/utils/rhf-inputs/country-selection-input.jsx +++ b/src/components/utils/rhf-inputs/country-selection-input.tsx @@ -5,19 +5,19 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -import { AutocompleteInput } from '@gridsuite/commons-ui'; +import { AutocompleteInput, AutocompleteInputProps } from '@gridsuite/commons-ui'; import { useLocalizedCountries } from '../localized-countries-hook'; -const CountrySelectionInput = ({ name, label, formProps, ...props }) => { +interface CountrySelectionInputProps extends Omit {} + +const CountrySelectionInput = (props: CountrySelectionInputProps) => { const { translate, countryCodes } = useLocalizedCountries(); return ( translate(countryCode)} + // TODO: the way Option is managed in AutocompleteInput is confusing, maybe make AutocompleteInput more generic in the future + getOptionLabel={(countryCode) => translate(countryCode as string)} {...props} /> ); diff --git a/src/components/utils/rhf-inputs/enum-input.jsx b/src/components/utils/rhf-inputs/enum-input.tsx similarity index 82% rename from src/components/utils/rhf-inputs/enum-input.jsx rename to src/components/utils/rhf-inputs/enum-input.tsx index c3acd1b941..26ec1a123d 100644 --- a/src/components/utils/rhf-inputs/enum-input.jsx +++ b/src/components/utils/rhf-inputs/enum-input.tsx @@ -6,11 +6,20 @@ */ import PropTypes from 'prop-types'; -import { FormControl, InputLabel, Select, MenuItem, FormHelperText } from '@mui/material'; +import { FormControl, InputLabel, Select, MenuItem, FormHelperText, FormControlTypeMap } from '@mui/material'; import { FormattedMessage } from 'react-intl'; import { FieldLabel } from '@gridsuite/commons-ui'; import { useController } from 'react-hook-form'; -const EnumInput = ({ options, name, label, size, labelValues }) => { + +interface EnumInputProps { + options: T[]; + name: string; + label: string; + size: 'small' | 'medium'; + labelValues: Record +} + +const EnumInput = ({ options, name, label, size, labelValues }: EnumInputProps) => { const { field: { onChange, value }, fieldState: { error }, diff --git a/src/components/utils/rhf-inputs/expandable-input/expandable-input.tsx b/src/components/utils/rhf-inputs/expandable-input/expandable-input.tsx index e1b6f255b2..a561330676 100644 --- a/src/components/utils/rhf-inputs/expandable-input/expandable-input.tsx +++ b/src/components/utils/rhf-inputs/expandable-input/expandable-input.tsx @@ -11,8 +11,7 @@ import AddIcon from '@mui/icons-material/ControlPoint'; import { FormattedMessage } from 'react-intl'; import { FunctionComponent } from 'react'; import { styles } from '../../../dialogs/dialog-utils'; -import { ErrorInput, MidFormError } from '@gridsuite/commons-ui'; -import { mergeSx } from '../../functions'; +import { ErrorInput, mergeSx, MidFormError } from '@gridsuite/commons-ui'; import { DeletableRow } from './deletable-row'; export interface ExpandableInputProps { diff --git a/src/components/utils/rhf-inputs/read-only/button-read-only-input.jsx b/src/components/utils/rhf-inputs/read-only/button-read-only-input.tsx similarity index 90% rename from src/components/utils/rhf-inputs/read-only/button-read-only-input.jsx rename to src/components/utils/rhf-inputs/read-only/button-read-only-input.tsx index 35e002b2d7..6f75136e3b 100644 --- a/src/components/utils/rhf-inputs/read-only/button-read-only-input.jsx +++ b/src/components/utils/rhf-inputs/read-only/button-read-only-input.tsx @@ -10,8 +10,14 @@ import { InputAdornment, TextField } from '@mui/material'; import { genHelperError } from '@gridsuite/commons-ui'; import PropTypes from 'prop-types'; import { useTheme } from '@mui/material'; +import { PropsWithChildren } from 'react'; -export function ButtonReadOnlyInput({ name, isNumerical = false, children }) { +interface ButtonReadOnlyInputProps extends PropsWithChildren{ + name: string; + isNumerical?: boolean; +} + +export function ButtonReadOnlyInput({ name, isNumerical = false, children }: ButtonReadOnlyInputProps) { const theme = useTheme(); const { diff --git a/src/components/utils/rhf-inputs/read-only/read-only-input.jsx b/src/components/utils/rhf-inputs/read-only/read-only-input.tsx similarity index 86% rename from src/components/utils/rhf-inputs/read-only/read-only-input.jsx rename to src/components/utils/rhf-inputs/read-only/read-only-input.tsx index cfb286fac3..11d3ae41c5 100644 --- a/src/components/utils/rhf-inputs/read-only/read-only-input.jsx +++ b/src/components/utils/rhf-inputs/read-only/read-only-input.tsx @@ -9,7 +9,12 @@ import { useController } from 'react-hook-form'; import { TextField } from '@mui/material'; import { genHelperError } from '@gridsuite/commons-ui'; -export function ReadOnlyInput({ name, isNumerical = false }) { +interface ReadOnlyInputProps { + name: string; + isNumerical?: boolean; +} + +export function ReadOnlyInput({ name, isNumerical = false }: ReadOnlyInputProps) { const { field: { value }, fieldState: { error }, diff --git a/src/components/utils/rhf-inputs/table-inputs/table-numerical-input.jsx b/src/components/utils/rhf-inputs/table-inputs/table-numerical-input.tsx similarity index 80% rename from src/components/utils/rhf-inputs/table-inputs/table-numerical-input.jsx rename to src/components/utils/rhf-inputs/table-inputs/table-numerical-input.tsx index ec96320b12..5f50334799 100644 --- a/src/components/utils/rhf-inputs/table-inputs/table-numerical-input.jsx +++ b/src/components/utils/rhf-inputs/table-inputs/table-numerical-input.tsx @@ -5,12 +5,21 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -import { IconButton, InputAdornment, TextField } from '@mui/material'; +import { IconButton, InputAdornment, InputBaseComponentProps, StandardTextFieldProps, TextField, TextFieldProps } from '@mui/material'; import { useController, useFormContext } from 'react-hook-form'; import ClearIcon from '@mui/icons-material/Clear'; import { useMemo } from 'react'; import { validateValueIsANumber } from 'components/utils/validation-functions'; +interface TableNumericalInputProps extends StandardTextFieldProps { + name: string; + inputProps: InputBaseComponentProps; + previousValue: number; + valueModified: boolean; + adornment: {text: string}; + isClearable?: boolean; +} + export const TableNumericalInput = ({ name, style, @@ -20,18 +29,19 @@ export const TableNumericalInput = ({ adornment, isClearable = true, ...props -}) => { +}: TableNumericalInputProps) => { const { trigger } = useFormContext(); const { field: { onChange, value, ref }, fieldState: { error }, } = useController({ name }); - const inputTransform = (value) => { + const inputTransform = (value: string) => { if (['-', '.'].includes(value)) { return value; } - return value === null || isNaN(value) ? '' : value.toString(); + const numericValue = Number(value); + return numericValue === null || isNaN(numericValue) ? '' : numericValue.toString(); }; const clearable = useMemo( @@ -44,7 +54,7 @@ export const TableNumericalInput = ({ [isClearable, previousValue, value] ); - const outputTransform = (value) => { + const outputTransform = (value: string | number) => { if (typeof value === 'string') { if (value === '-') { return value; @@ -62,7 +72,7 @@ export const TableNumericalInput = ({ return value === Number.MAX_VALUE ? null : value; }; - const handleInputChange = (e) => { + const handleInputChange = (e: React.ChangeEvent) => { onChange(outputTransform(e.target.value)); trigger(name); }; @@ -86,8 +96,8 @@ export const TableNumericalInput = ({ fontSize: 'small', color: previousValue !== undefined && previousValue === parseFloat(value) && !valueModified - ? 'grey' - : null, // grey out the value if it is the same as the previous one + ? 'grey' + : undefined, // grey out the value if it is the same as the previous one textAlign: style?.textAlign ?? 'left', }, inputMode: 'numeric', diff --git a/src/components/utils/rhf-inputs/table-inputs/table-text-input.jsx b/src/components/utils/rhf-inputs/table-inputs/table-text-input.tsx similarity index 74% rename from src/components/utils/rhf-inputs/table-inputs/table-text-input.jsx rename to src/components/utils/rhf-inputs/table-inputs/table-text-input.tsx index b5a3f64cb7..c70547a4c2 100644 --- a/src/components/utils/rhf-inputs/table-inputs/table-text-input.jsx +++ b/src/components/utils/rhf-inputs/table-inputs/table-text-input.tsx @@ -5,11 +5,17 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -import { TextField } from '@mui/material'; +import { InputBaseComponentProps, TextField } from '@mui/material'; import { useController } from 'react-hook-form'; import { useIntl } from 'react-intl'; -export const TableTextInput = ({ name, style, showErrorMsg, inputProps, ...props }) => { +interface TableTextInputProps { + name: string; + showErrorMsg: boolean; + inputProps: InputBaseComponentProps; +} + +export const TableTextInput = ({ name, showErrorMsg, inputProps, ...props }: TableTextInputProps) => { const { field: { onChange, value, ref }, fieldState: { error }, @@ -17,11 +23,11 @@ export const TableTextInput = ({ name, style, showErrorMsg, inputProps, ...props const intl = useIntl(); - const outputTransform = (value) => { + const outputTransform = (value: string) => { return value?.trim() === '' ? '' : value; }; - const handleInputChange = (e) => { + const handleInputChange = (e: React.ChangeEvent) => { onChange(outputTransform(e.target.value)); }; diff --git a/src/components/utils/split-button.jsx b/src/components/utils/split-button.tsx similarity index 87% rename from src/components/utils/split-button.jsx rename to src/components/utils/split-button.tsx index 8919daca33..7c2c1d77da 100644 --- a/src/components/utils/split-button.jsx +++ b/src/components/utils/split-button.tsx @@ -23,12 +23,14 @@ import DoneIcon from '@mui/icons-material/Done'; import ErrorOutlineIcon from '@mui/icons-material/ErrorOutline'; import PlayIcon from '@mui/icons-material/PlayArrow'; import RunningStatus from './running-status'; -import { mergeSx } from './functions'; import { useSelector } from 'react-redux'; import { useRef, useState } from 'react'; +import { AppState } from 'redux/reducer'; +import { Theme } from '@mui/material'; +import { mergeSx } from '@gridsuite/commons-ui'; const styles = { - expand: (theme) => ({ + expand: (theme: Theme) => ({ marginLeft: 'auto', transition: theme.transitions.create('transform', { duration: theme.transitions.duration.shortest, @@ -37,7 +39,7 @@ const styles = { expandOpen: { transform: 'rotate(180deg)', }, - listOptions: (theme) => ({ + listOptions: (theme: Theme) => ({ minWidth: '270px', marginRight: '43px', position: 'relative', @@ -89,7 +91,7 @@ const styles = { color: '#fdfdfd', }, }, - running: (theme) => ({ + running: (theme: Theme) => ({ backgroundColor: theme.palette.background.default, color: theme.palette.text.primary, border: '1px solid #808080', @@ -105,7 +107,7 @@ const styles = { color: theme.palette.text.primary, }, }), - idle: (theme) => ({ + idle: (theme: Theme) => ({ backgroundColor: theme.palette.background.default, color: theme.palette.text.primary, borderColor: '#808080', @@ -128,6 +130,19 @@ const styles = { }, }; +interface SplitButtonProps { + runningStatus: RunningStatus; + buttonDisabled?: boolean; + selectionDisabled?: boolean; + computationStopped: boolean; + text: string; + options: string[]; + selectedIndex: number; + onClick: () => void; + actionOnRunnable: () => void; + onSelectionChange: (index: number) => void; +} + const SplitButton = ({ runningStatus, buttonDisabled = false, @@ -139,11 +154,11 @@ const SplitButton = ({ onClick, actionOnRunnable, onSelectionChange, -}) => { +}: SplitButtonProps) => { const [open, setOpen] = useState(false); - const computationStarting = useSelector((state) => state.computationStarting); + const computationStarting = useSelector((state: AppState) => state.computationStarting); - const anchorRef = useRef(null); + const anchorRef = useRef(null); const handleClick = () => { if (onClick) { @@ -151,7 +166,7 @@ const SplitButton = ({ } }; - const handleMenuItemClick = (event, index) => { + const handleMenuItemClick = (event: React.MouseEvent, index: number) => { if (runningStatus === RunningStatus.RUNNING) { actionOnRunnable(); } else { @@ -164,14 +179,15 @@ const SplitButton = ({ setOpen((prevOpen) => !prevOpen); }; - const handleClose = (event) => { - if (anchorRef.current && anchorRef.current.contains(event.target)) { + const handleClose = (event: MouseEvent | TouchEvent) => { + // after doing some researches, casting event.target as HTMLElement seems to be the way to type this + if (anchorRef.current && anchorRef.current.contains(event.target as HTMLElement)) { return; } setOpen(false); }; - const getRunningIcon = (status) => { + const getRunningIcon = (status: RunningStatus) => { switch (status) { case RunningStatus.RUNNING: return ; @@ -185,7 +201,7 @@ const SplitButton = ({ } }; - const getStyle = (runningStatus) => { + const getStyle = (runningStatus: RunningStatus) => { switch (runningStatus) { case RunningStatus.SUCCEED: return styles.succeed; @@ -199,7 +215,7 @@ const SplitButton = ({ } }; - const breakText = (text) => { + const breakText = (text: string) => { return text.split('\n').map((text, i) => (i ? [
, text] : text)); }; @@ -226,7 +242,7 @@ const SplitButton = ({ sx={getStyle(runningStatus)} disabled={selectionDisabled} > - + diff --git a/src/components/utils/utils.test.js b/src/components/utils/utils.test.ts similarity index 100% rename from src/components/utils/utils.test.js rename to src/components/utils/utils.test.ts diff --git a/src/components/utils/utils.js b/src/components/utils/utils.ts similarity index 66% rename from src/components/utils/utils.js rename to src/components/utils/utils.ts index bb27f58929..3d063e541a 100644 --- a/src/components/utils/utils.js +++ b/src/components/utils/utils.ts @@ -6,10 +6,12 @@ */ import { useEffect, useRef } from 'react'; -import { getIn } from 'yup'; +import { getIn, SchemaDescription } from 'yup'; import { isBlankOrEmpty, toNumber } from './validation-functions'; import { CURRENT_LIMITS, ID } from './field-constants'; import { addSelectedFieldToRows } from './dnd-table/dnd-table'; +import { CurrentLimits, OperationalLimitsGroup, TemporaryLimit } from 'services/network-modification-types'; +import { VoltageLevel } from './equipment-types'; export const UNDEFINED_ACCEPTABLE_DURATION = Math.pow(2, 31) - 1; @@ -19,7 +21,7 @@ export const UNDEFINED_ACCEPTABLE_DURATION = Math.pow(2, 31) - 1; * @param {string} id - The id of the enum value * @returns {string | undefined} - The label of the enum value */ -export const getEnumLabelById = (enumValues, id) => { +export const getEnumLabelById = (enumValues: { id: string; label: string }[], id: string) => { if (!enumValues || !id) { return undefined; } @@ -27,15 +29,15 @@ export const getEnumLabelById = (enumValues, id) => { return enumValue?.label; }; -export const isFieldRequired = (fieldName, schema, values) => { +export const isFieldRequired = (fieldName: string, schema: any, values: unknown) => { const { schema: fieldSchema, parent: parentValues } = getIn(schema, fieldName, values) || {}; - return fieldSchema.describe({ parent: parentValues })?.optional === false; + return (fieldSchema.describe({ parent: parentValues }) as SchemaDescription)?.optional === false; - //static way, not working when using "when" in schema, but does not need form values - //return yup.reach(schema, fieldName)?.exclusiveTests?.required === true; + // static way, not working when using "when" in schema, but does not need form values + // return yup.reach(schema, fieldName)?.exclusiveTests?.required === true; }; -export const areArrayElementsUnique = (array) => { +export const areArrayElementsUnique = (array: unknown[]) => { let uniqueAlphaValues = [...new Set(array)]; return uniqueAlphaValues.length === array.length; }; @@ -45,7 +47,7 @@ export const areArrayElementsUnique = (array) => { * @param array or numbers * @returns {boolean} */ -export const areNumbersOrdered = (array) => { +export const areNumbersOrdered = (array?: unknown) => { if (!Array.isArray(array)) { return false; } @@ -81,28 +83,19 @@ export const areNumbersOrdered = (array) => { return true; }; -export const findIndexesOfDuplicateFieldValues = (values, fieldName) => { - const counts = new Map(); - values.forEach((element, index) => { - const value = element[fieldName]; - counts.set(value, (counts.get(value) || []).concat(index)); - }); - return [...counts.values()].filter((indexes) => indexes.length > 1).flat(); -}; - -export const areIdsEqual = (val1, val2) => { +export const areIdsEqual = (val1: { id: string }, val2: { id: string }) => { return val1.id === val2.id; }; -export const areUuidsEqual = (val1, val2) => { +export const areUuidsEqual = (val1: { uuid: string }, val2: { uuid: string }) => { return val1.uuid === val2.uuid; }; -export const getObjectId = (object) => { +export const getObjectId = (object: string | { id: string }) => { return typeof object === 'string' ? object : object?.id ?? null; }; -export const buildNewBusbarSections = (equipmentId, sectionCount, busbarCount) => { +export const buildNewBusbarSections = (equipmentId: string, sectionCount: number, busbarCount: number) => { const newBusbarSections = []; for (let i = 0; i < busbarCount; i++) { for (let j = 0; j < sectionCount; j++) { @@ -115,18 +108,18 @@ export const buildNewBusbarSections = (equipmentId, sectionCount, busbarCount) = return newBusbarSections; }; -export function toModificationOperation(value) { +export function toModificationOperation(value: unknown) { return value === 0 || value === false || value ? { value: value, op: 'SET' } : null; } -export function toModificationUnsetOperation(value) { +export function toModificationUnsetOperation(value: unknown) { if (value === null) { return null; } return value === 0 || value === false || value ? { value: value, op: 'SET' } : { op: 'UNSET' }; } -export const formatTemporaryLimits = (temporaryLimits) => +export const formatTemporaryLimits = (temporaryLimits?: TemporaryLimit[]) => temporaryLimits?.map((limit) => { return { name: limit?.name ?? '', @@ -136,16 +129,18 @@ export const formatTemporaryLimits = (temporaryLimits) => }; }); -export const formatCompleteCurrentLimit = (completeLimitsGroups /*: OperationalLimitsGroup[]*/) => { - const formattedCompleteLimitsGroups /*: OperationalLimitsGroup[]*/ = []; +export const formatCompleteCurrentLimit = (completeLimitsGroups: OperationalLimitsGroup[]) => { + const formattedCompleteLimitsGroups: OperationalLimitsGroup[] = []; if (completeLimitsGroups) { completeLimitsGroups.forEach((elt) => { if (!isBlankOrEmpty(elt.id)) { formattedCompleteLimitsGroups.push({ [ID]: elt.id, [CURRENT_LIMITS]: { - permanentLimit: elt.permanentLimit, - temporaryLimits: addSelectedFieldToRows(formatTemporaryLimits(elt?.temporaryLimits)), + permanentLimit: elt.currentLimits.permanentLimit, //TODO: check change is ok ? + temporaryLimits: addSelectedFieldToRows( + formatTemporaryLimits(elt?.currentLimits.temporaryLimits) + ), }, }); } @@ -154,14 +149,17 @@ export const formatCompleteCurrentLimit = (completeLimitsGroups /*: OperationalL return formattedCompleteLimitsGroups; }; -export const richTypeEquals = (a, b) => a === b; +export const richTypeEquals = (a: unknown, b: unknown) => a === b; -export const computeHighTapPosition = (steps) => { +export const computeHighTapPosition = (steps: { index: number }[]) => { const values = steps?.map((step) => step['index']); return values?.length > 0 ? Math.max(...values) : null; }; -export const compareStepsWithPreviousValues = (tapSteps, previousValues) => { +export const compareStepsWithPreviousValues = ( + tapSteps: Record[], + previousValues: Record[] +) => { if (previousValues === undefined) { return false; } @@ -171,12 +169,18 @@ export const compareStepsWithPreviousValues = (tapSteps, previousValues) => { return tapSteps.every((step, index) => { const previousStep = previousValues[index]; return Object.getOwnPropertyNames(previousStep).every((key) => { - return parseFloat(step[key]) === previousStep[key]; + return step[key] === previousStep[key]; }); }); }; -export const getTapChangerEquipmentSectionTypeValue = (tapChanger) => { +interface TapChangerInfos { + regulatingTerminalConnectableType: string; + regulatingTerminalConnectableId: string; + regulatingTerminalVlId: string; +} + +export const getTapChangerEquipmentSectionTypeValue = (tapChanger: TapChangerInfos) => { if (!tapChanger?.regulatingTerminalConnectableType) { return null; } else { @@ -184,7 +188,7 @@ export const getTapChangerEquipmentSectionTypeValue = (tapChanger) => { } }; -export const getTapChangerRegulationTerminalValue = (tapChanger) => { +export const getTapChangerRegulationTerminalValue = (tapChanger: TapChangerInfos) => { let regulatingTerminalGeneratorValue = tapChanger?.regulatingTerminalConnectableId ?? ''; if (tapChanger?.regulatingTerminalVlId) { regulatingTerminalGeneratorValue += ' ( ' + tapChanger?.regulatingTerminalVlId + ' )'; @@ -192,59 +196,52 @@ export const getTapChangerRegulationTerminalValue = (tapChanger) => { return regulatingTerminalGeneratorValue; }; -export function calculateResistance(distance, linearResistance) { +export function calculateResistance(distance: number, linearResistance: number) { if (distance === undefined || isNaN(distance) || linearResistance === undefined || isNaN(linearResistance)) { return 0; } return Number(distance) * Number(linearResistance); } -export function calculateReactance(distance, linearReactance) { +export function calculateReactance(distance: number, linearReactance: number) { if (distance === undefined || isNaN(distance) || linearReactance === undefined || isNaN(linearReactance)) { return 0; } return Number(distance) * Number(linearReactance); } -export const computeSwitchedOnValue = (sectionCount, maximumSectionCount, linkedSwitchedOnValue) => { +export const computeSwitchedOnValue = ( + sectionCount: number, + maximumSectionCount: number, + linkedSwitchedOnValue: number +) => { return (linkedSwitchedOnValue / maximumSectionCount) * sectionCount; }; -export const computeQAtNominalV = (susceptance, nominalVoltage) => { +export const computeQAtNominalV = (susceptance: number, nominalVoltage: number) => { return Math.abs(susceptance * Math.pow(nominalVoltage, 2)); }; -export const computeMaxQAtNominalV = (maxSucepctance, nominalVoltage) => { +export const computeMaxQAtNominalV = (maxSucepctance: number, nominalVoltage: number) => { return Math.abs(maxSucepctance * Math.pow(nominalVoltage, 2)); }; -export const computeMaxSusceptance = (maxQAtNominalV, nominalVoltage) => { +export const computeMaxSusceptance = (maxQAtNominalV: number, nominalVoltage: number) => { return Math.abs(maxQAtNominalV / Math.pow(nominalVoltage, 2)); }; -export function calculateSusceptance(distance, linearCapacity) { +export function calculateSusceptance(distance: number, linearCapacity: number) { if (distance === undefined || isNaN(distance) || linearCapacity === undefined || isNaN(linearCapacity)) { return 0; } return Number(distance) * Number(linearCapacity) * 2 * Math.PI * 50 * Math.pow(10, 6); } -export const replaceAllDefaultValues = (arrayParams, oldValue, newValue) => { - return ( - arrayParams && - arrayParams.reduce((accumulator, current) => { - return [ - ...accumulator, - { - ...current, - defaultValue: current.defaultValue === oldValue ? newValue : current.defaultValue, - }, - ]; - }, []) - ); -}; - -export function getNewVoltageLevelOptions(formattedVoltageLevel, oldVoltageLevelId, voltageLevelOptions) { +export function getNewVoltageLevelOptions( + formattedVoltageLevel: VoltageLevel, + oldVoltageLevelId: string, + voltageLevelOptions: VoltageLevel[] +) { const newVoltageLevelOptions = formattedVoltageLevel.id === oldVoltageLevelId ? voltageLevelOptions.filter((vl) => vl.id !== formattedVoltageLevel.id) @@ -254,17 +251,9 @@ export function getNewVoltageLevelOptions(formattedVoltageLevel, oldVoltageLevel return newVoltageLevelOptions; } -export function usePrevious(value) { - const ref = useRef(); - useEffect(() => { - ref.current = value; - }, [value]); - return ref.current; -} - // remove elementToToggle from list, or add it if it does not exist yet // useful when checking/unchecking checkboxex -export function toggleElementFromList(elementToToggle, list, getFieldId) { +export function toggleElementFromList(elementToToggle: T, list: T[], getFieldId: (element: T) => string) { const resultList = [...list]; const elementToToggleIndex = resultList.findIndex((element) => getFieldId(element) === getFieldId(elementToToggle)); if (elementToToggleIndex >= 0) { @@ -275,7 +264,7 @@ export function toggleElementFromList(elementToToggle, list, getFieldId) { return resultList; } -export const comparatorStrIgnoreCase = (str1, str2) => { +export const comparatorStrIgnoreCase = (str1: string, str2: string) => { return str1.toLowerCase().localeCompare(str2.toLowerCase()); }; diff --git a/src/components/utils/validation-functions.test.js b/src/components/utils/validation-functions.test.ts similarity index 82% rename from src/components/utils/validation-functions.test.js rename to src/components/utils/validation-functions.test.ts index 480b835298..fc0decf2e3 100644 --- a/src/components/utils/validation-functions.test.js +++ b/src/components/utils/validation-functions.test.ts @@ -11,7 +11,6 @@ import { validateValueIsLessThanOrEqualTo, validateValueIsLessThan, validateField, - checkReactiveCapabilityCurve, } from './validation-functions'; const { toNumber, isBlankOrEmpty } = exportedForTesting; @@ -313,101 +312,6 @@ test('validation-functions.validateField.valueGreaterThan', () => { expect(validateField(undefined, { valueGreaterThan: 2, isFieldRequired: true }).error).toBe(true); }); -test('validation-functions.checkReactiveCapabilityCurve', () => { - // Reactive capability curve default format : [{ p: '', minQ: '', maxQ: '' }, { p: '', minQ: '', maxQ: '' }] - - // Correct reactive cabability curves - expect( - checkReactiveCapabilityCurve([ - { p: '0', minQ: '0', maxQ: '0' }, - { p: '10', minQ: '0', maxQ: '0' }, - ]).length - ).toBe(0); - expect( - checkReactiveCapabilityCurve([ - { p: '-10', minQ: '-5', maxQ: '-2' }, - { p: '10', minQ: '1', maxQ: '56' }, - ]).length - ).toBe(0); - expect( - checkReactiveCapabilityCurve([ - { p: '-10', minQ: '-5', maxQ: '-2' }, - { p: '0', minQ: '0', maxQ: '0' }, - { p: '10', minQ: '1', maxQ: '56' }, - ]).length - ).toBe(0); - expect( - checkReactiveCapabilityCurve([ - { p: '-10', minQ: '-5', maxQ: '-2' }, - { p: '0', minQ: '0,8', maxQ: '1' }, - { p: '-3', minQ: '-6.5', maxQ: '-2' }, - { p: '10', minQ: '1', maxQ: '56' }, - ]).length - ).toBe(0); - - // Not enough points - expect(checkReactiveCapabilityCurve([]).length).not.toBe(0); - expect(checkReactiveCapabilityCurve([{ p: '0', minQ: '0', maxQ: '0' }]).length).not.toBe(0); - - // Not unique P values - expect( - checkReactiveCapabilityCurve([ - { p: '10', minQ: '-5', maxQ: '-2' }, - { p: '10', minQ: '1', maxQ: '56' }, - ]).length - ).not.toBe(0); - expect( - checkReactiveCapabilityCurve([ - { p: '-10', minQ: '-5', maxQ: '-2' }, - { p: '-0', minQ: '0', maxQ: '0' }, - { p: '0', minQ: '1', maxQ: '56' }, - ]).length - ).not.toBe(0); - expect( - checkReactiveCapabilityCurve([ - { p: '-0', minQ: '0', maxQ: '0' }, - { p: '0', minQ: '0', maxQ: '0' }, - ]).length - ).not.toBe(0); - expect( - checkReactiveCapabilityCurve([ - { p: '0', minQ: '0', maxQ: '0' }, - { p: '0.0', minQ: '0', maxQ: '0' }, - ]).length - ).not.toBe(0); - expect( - checkReactiveCapabilityCurve([ - { p: ',0', minQ: '0', maxQ: '0' }, - { p: '0', minQ: '0', maxQ: '0' }, - ]).length - ).not.toBe(0); - - // Pmin and Pmax values are not in the begining and end of the array - expect( - checkReactiveCapabilityCurve([ - { p: '0', minQ: '-5', maxQ: '-2' }, - { p: '-10', minQ: '0', maxQ: '0' }, - { p: '10', minQ: '1', maxQ: '56' }, - ]).length - ).not.toBe(0); - - // P values between Pmin and Pmax are below Pmin or above Pmax - expect( - checkReactiveCapabilityCurve([ - { p: '-10', minQ: '-5', maxQ: '-2' }, - { p: '260', minQ: '0', maxQ: '0' }, - { p: '10', minQ: '1', maxQ: '56' }, - ]).length - ).not.toBe(0); - expect( - checkReactiveCapabilityCurve([ - { p: '-10', minQ: '-5', maxQ: '-2' }, - { p: '-20', minQ: '0', maxQ: '0' }, - { p: '10', minQ: '1', maxQ: '56' }, - ]).length - ).not.toBe(0); -}); - test('validation-functions.validateField.forceValidation', () => { expect(validateField(600, { valueLessThan: 10 }).error).toBe(true); expect(validateField(600, { valueLessThan: 10 }, true).error).toBe(false); diff --git a/src/components/utils/validation-functions.js b/src/components/utils/validation-functions.ts similarity index 63% rename from src/components/utils/validation-functions.js rename to src/components/utils/validation-functions.ts index 9066ba906c..53d246e811 100644 --- a/src/components/utils/validation-functions.js +++ b/src/components/utils/validation-functions.ts @@ -5,6 +5,8 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +import { ReactiveCapabilityCurvePoint } from "components/dialogs/network-modifications/hvdc-line/vsc/converter-station/converter-station-utils"; + const NO_ERROR = { error: false, errorMsgId: null, @@ -14,7 +16,7 @@ const NO_ERROR = { * Returns a Number corresponding to provided value, or NaN if not a valid number per * Gridsuite's standard (allows either coma or dots for decimal). */ -export function toNumber(value) { +export function toNumber(value: any) { if (typeof value === 'number') { return value; } else if (typeof value === 'string') { @@ -31,7 +33,7 @@ export function toNumber(value) { * Returns true if value is either undefined, null, empty or only contains whitespaces. * Otherwise, if value is a boolean or a number, returns false. */ -export function isBlankOrEmpty(value) { +export function isBlankOrEmpty(value: unknown) { if (value === undefined || value === null) { return true; } @@ -44,7 +46,7 @@ export function isBlankOrEmpty(value) { /* * Returns true if the value is a valid number, per Gridsuite's standard (allows either coma or dots for decimal). */ -export function validateValueIsANumber(value) { +export function validateValueIsANumber(value?: string | number | null | boolean) : value is number{ if (value == null || value === '') { return false; } @@ -57,7 +59,7 @@ export function validateValueIsANumber(value) { * - the second parameter valueToCompareTo is a valid number * - the first parameter's value is lower or equal to the second's */ -export function validateValueIsLessThanOrEqualTo(value, valueToCompareTo) { +export function validateValueIsLessThanOrEqualTo(value?: string | number | null | boolean, valueToCompareTo?: string | number | null | boolean){ return ( validateValueIsANumber(value) && validateValueIsANumber(valueToCompareTo) && @@ -71,7 +73,7 @@ export function validateValueIsLessThanOrEqualTo(value, valueToCompareTo) { * - the second parameter valueToCompareTo is a valid number * - the first parameter's value is greater or equal to the second's */ -export function validateValueIsGreaterThanOrEqualTo(value, valueToCompareTo) { +export function validateValueIsGreaterThanOrEqualTo(value?: string | number | null | boolean, valueToCompareTo?: string | number | null | boolean) { return ( validateValueIsANumber(value) && validateValueIsANumber(valueToCompareTo) && @@ -85,7 +87,7 @@ export function validateValueIsGreaterThanOrEqualTo(value, valueToCompareTo) { * - the second parameter valueToCompareTo is a valid number * - the first parameter's value is lower than the second's */ -export function validateValueIsLessThan(value, valueToCompareTo) { +export function validateValueIsLessThan(value?: string | number | null | boolean, valueToCompareTo?: string | number | null | boolean) { return ( validateValueIsANumber(value) && validateValueIsANumber(valueToCompareTo) && @@ -99,7 +101,7 @@ export function validateValueIsLessThan(value, valueToCompareTo) { * - the second parameter valueToCompareTo is a valid number * - the first parameter's value is greater than the second's */ -export function validateValueIsGreaterThan(value, valueToCompareTo) { +export function validateValueIsGreaterThan(value?: string | number | null | boolean, valueToCompareTo?: string | number | null | boolean) { return ( validateValueIsANumber(value) && validateValueIsANumber(valueToCompareTo) && @@ -107,11 +109,22 @@ export function validateValueIsGreaterThan(value, valueToCompareTo) { ); } +interface ToValidateType { + isFieldRequired?: boolean; + forceValidation?: boolean; + isFieldNumeric?: boolean; + valueLessThanOrEqualTo?: number; + valueGreaterThanOrEqualTo?: number; + valueLessThan?: number; + valueGreaterThan?: number; + errorMsgId?: string; +} + /* * Rule : if the field is NOT required (toValidate.isFieldRequired is either undefined or equals to false), * then any check that applies to the value will pass if the value is empty. */ -export function validateField(value, toValidate, disabled = false) { +export function validateField(value: string | number | null | undefined | boolean, toValidate: ToValidateType, disabled = false) { if (disabled && !toValidate.forceValidation) { return NO_ERROR; } @@ -152,58 +165,7 @@ export function validateField(value, toValidate, disabled = false) { return NO_ERROR; } -/** - * Checks if the provided reactive capabilty curve is valid. Returns a list of - * errors if any, or an empty array otherwise. - * @param reactiveCapabilityCurve an array of reactive capability curve points of - * this format : [{ p: '', minQ: '', maxQ: '' }, { p: '', minQ: '', maxQ: '' }] - * @returns An array of error messages. If there is no error, returns an empty array. - */ -export function checkReactiveCapabilityCurve(reactiveCapabilityCurve) { - let errorMessages = []; - - // At least four points must be set - if (reactiveCapabilityCurve.length < 2) { - errorMessages.push('ReactiveCapabilityCurveCreationErrorMissingPoints'); - } - - // Each P must be a unique valid number - const everyValidP = reactiveCapabilityCurve - .map((element) => - // Note : convertion toNumber is necessary here to prevent corner cases like if - // two values are "-0" and "0", which would be considered different by the Set below. - validateValueIsANumber(element.p) ? toNumber(element.p) : null - ) - .filter((p) => p !== null); - const setOfPs = [...new Set(everyValidP)]; - - if (setOfPs.length !== everyValidP.length) { - errorMessages.push('ReactiveCapabilityCurveCreationErrorPInvalid'); - } else { - // The first P must be the lowest value - // The last P must be the highest value - // The P in between must be in the range defined by the first and last P - const minP = everyValidP[0]; - const maxP = everyValidP[everyValidP.length - 1]; - const pAreInRange = everyValidP.filter( - (p) => validateValueIsLessThanOrEqualTo(minP, p) && validateValueIsLessThanOrEqualTo(p, maxP) - ); - if (pAreInRange.length !== everyValidP.length) { - errorMessages.push('ReactiveCapabilityCurveCreationErrorPOutOfRange'); - } - } - - // Each qMin must be inferior or equal to qMax - for (let element of reactiveCapabilityCurve) { - if (!validateValueIsLessThanOrEqualTo(element.minQ, element.maxQ)) { - errorMessages.push('ReactiveCapabilityCurveCreationErrorQminPQmaxPIncoherence'); - break; - } - } - return errorMessages; -} - -function makeErrorRecord(msgId) { +function makeErrorRecord(msgId?: string) { if (msgId === undefined) { console.warn('Error message id missing in validation function !'); } diff --git a/src/components/utils/waiting-loader.jsx b/src/components/utils/waiting-loader.tsx similarity index 85% rename from src/components/utils/waiting-loader.jsx rename to src/components/utils/waiting-loader.tsx index b838826bdd..1618e018c4 100644 --- a/src/components/utils/waiting-loader.jsx +++ b/src/components/utils/waiting-loader.tsx @@ -9,8 +9,15 @@ import LoaderWithOverlay from './loader-with-overlay'; import PageNotFound from '../page-not-found'; import PropTypes from 'prop-types'; import Paper from '@mui/material/Paper'; +import { PropsWithChildren } from 'react'; -const WaitingLoader = ({ loading, message, errMessage, children }) => { +interface WaitingLoaderProps extends PropsWithChildren { + loading?: boolean; + message: string; + errMessage?: string; +} + +const WaitingLoader = ({ loading, message, errMessage, children }: WaitingLoaderProps) => { if (errMessage !== undefined) { /* TODO errMessage -> error {status, message} to get 404, 403 and adapt Page*/ return ; From 081eee24c6751c8d0f4777d967e04d0293b25560 Mon Sep 17 00:00:00 2001 From: LE SAULNIER Kevin Date: Mon, 24 Feb 2025 09:07:10 +0100 Subject: [PATCH 2/6] feat: migrate more and more code Signed-off-by: LE SAULNIER Kevin --- .../dialogs/limits/limits-side-pane.tsx | 18 +- .../dialogs/limits/temporary-limits-table.tsx | 23 +- .../phase-tap-changer-pane-steps.jsx | 2 + .../ratio-tap-changer-pane-steps.jsx | 12 +- .../voltageinit/voltage-limits-parameters.tsx | 31 +-- .../custom-columns/node-alias-table.tsx | 12 +- ...alog.jsx => dnd-table-add-rows-dialog.tsx} | 15 +- ....jsx => dnd-table-bottom-left-buttons.tsx} | 12 +- ...jsx => dnd-table-bottom-right-buttons.tsx} | 25 +- .../{dnd-table.jsx => dnd-table.tsx} | 228 +++++++++++++----- .../utils/rhf-inputs/chip-items-input.tsx | 2 +- .../table-inputs/table-numerical-input.tsx | 19 +- .../table-inputs/table-text-input.tsx | 4 +- 13 files changed, 271 insertions(+), 132 deletions(-) rename src/components/utils/dnd-table/{dnd-table-add-rows-dialog.jsx => dnd-table-add-rows-dialog.tsx} (89%) rename src/components/utils/dnd-table/{dnd-table-bottom-left-buttons.jsx => dnd-table-bottom-left-buttons.tsx} (88%) rename src/components/utils/dnd-table/{dnd-table-bottom-right-buttons.jsx => dnd-table-bottom-right-buttons.tsx} (91%) rename src/components/utils/dnd-table/{dnd-table.jsx => dnd-table.tsx} (71%) diff --git a/src/components/dialogs/limits/limits-side-pane.tsx b/src/components/dialogs/limits/limits-side-pane.tsx index 45a0063b45..804fdcbdd9 100644 --- a/src/components/dialogs/limits/limits-side-pane.tsx +++ b/src/components/dialogs/limits/limits-side-pane.tsx @@ -21,7 +21,7 @@ import { useFieldArray, useFormContext } from 'react-hook-form'; import { formatTemporaryLimits } from '../../utils/utils.js'; import { isNodeBuilt } from '../../graph/util/model-functions'; import { TemporaryLimit } from '../../../services/network-modification-types'; -import DndTable from '../../utils/dnd-table/dnd-table'; +import DndTable, { ColumnNumeric, ColumnText, DndColumn, DndColumnType } from '../../utils/dnd-table/dnd-table'; import TemporaryLimitsTable from './temporary-limits-table'; import { CurrentTreeNode } from '../../../redux/reducer'; @@ -58,14 +58,14 @@ export function LimitsSidePane({ const useFieldArrayOutputTemporaryLimits = useFieldArray({ name: `${limitsGroupFormName}.${TEMPORARY_LIMITS}`, }); - const columnsDefinition: ILimitColumnDef[] = useMemo(() => { + const columnsDefinition: ((ColumnText | ColumnNumeric) & { initialValue: any })[] = useMemo(() => { return [ { label: 'TemporaryLimitName', dataKey: TEMPORARY_LIMIT_NAME, initialValue: '', editable: true, - numeric: false, + type: DndColumnType.TEXT, maxWidth: 200, }, { @@ -73,7 +73,7 @@ export function LimitsSidePane({ dataKey: TEMPORARY_LIMIT_DURATION, initialValue: null, editable: true, - numeric: true, + type: DndColumnType.NUMERIC, maxWidth: 100, }, { @@ -81,7 +81,7 @@ export function LimitsSidePane({ dataKey: TEMPORARY_LIMIT_VALUE, initialValue: null, editable: true, - numeric: true, + type: DndColumnType.NUMERIC, maxWidth: 100, }, ].map((column) => ({ @@ -92,16 +92,16 @@ export function LimitsSidePane({ const newRowData = useMemo(() => { let newRowData: any = {}; - columnsDefinition.forEach((column: ILimitColumnDef) => (newRowData[column.dataKey] = column.initialValue)); + columnsDefinition.forEach((column) => (newRowData[column.dataKey] = column.initialValue)); return newRowData; }, [columnsDefinition]); const createRows = () => [newRowData]; const temporaryLimitHasPreviousValue = useCallback( - (rowIndex: number, arrayFormName: string, temporaryLimits: TemporaryLimit[]) => { + (rowIndex: number, arrayFormName: string, temporaryLimits?: TemporaryLimit[]) => { return ( formatTemporaryLimits(temporaryLimits)?.filter( - (l: TemporaryLimit) => + (l) => l.name === getValues(arrayFormName)[rowIndex]?.name && l.acceptableDuration === getValues(arrayFormName)[rowIndex]?.acceptableDuration )?.length > 0 @@ -111,7 +111,7 @@ export function LimitsSidePane({ ); const shouldReturnPreviousValue = useCallback( - (rowIndex: number, column: ILimitColumnDef, arrayFormName: string, temporaryLimits: TemporaryLimit[]) => { + (rowIndex: number, column: ILimitColumnDef, arrayFormName: string, temporaryLimits?: TemporaryLimit[]) => { return ( (temporaryLimitHasPreviousValue(rowIndex, arrayFormName, temporaryLimits) && column.dataKey === TEMPORARY_LIMIT_VALUE) || diff --git a/src/components/dialogs/limits/temporary-limits-table.tsx b/src/components/dialogs/limits/temporary-limits-table.tsx index f6f9927739..a3399c4802 100644 --- a/src/components/dialogs/limits/temporary-limits-table.tsx +++ b/src/components/dialogs/limits/temporary-limits-table.tsx @@ -17,6 +17,7 @@ import { TableNumericalInput } from '../../utils/rhf-inputs/table-inputs/table-n import { TableTextInput } from '../../utils/rhf-inputs/table-inputs/table-text-input'; import { ILimitColumnDef } from './limits-side-pane'; import { TemporaryLimit } from '../../../services/network-modification-types'; +import { ColumnNumeric, ColumnText, DndColumn, DndColumnType } from 'components/utils/dnd-table/dnd-table'; const styles = { columnsStyle: { @@ -30,16 +31,16 @@ const styles = { interface CustomTableCellProps { name: string; - column: ILimitColumnDef; + column: ColumnText | ColumnNumeric; disabled: boolean; - previousValue: number | string | null | undefined; + previousValue: number | undefined; valueModified: boolean; } function EditableTableCell({ name, column, previousValue, valueModified, ...props }: Readonly) { return ( - {column.numeric ? ( + {column.type === DndColumnType.NUMERIC ? ( ) : ( - + )} ); @@ -59,19 +56,19 @@ function EditableTableCell({ name, column, previousValue, valueModified, ...prop interface TemporaryLimitsTableProps { arrayFormName: string; - columnsDefinition: ILimitColumnDef[]; + columnsDefinition: (ColumnText | ColumnNumeric)[]; createRow: () => any[]; disabled?: boolean; previousValues: TemporaryLimit[]; disableTableCell: ( rowIndex: number, - column: ILimitColumnDef, + column: ColumnText | ColumnNumeric, arrayFormName: string, temporaryLimits: TemporaryLimit[] ) => boolean; getPreviousValue: ( rowIndex: number, - column: ILimitColumnDef, + column: ColumnText | ColumnNumeric, arrayFormName: string, temporaryLimits: TemporaryLimit[] ) => number | undefined; @@ -93,7 +90,7 @@ function TemporaryLimitsTable({ const { fields, append, remove } = useFieldArray({ name: arrayFormName }); const [hoveredRowIndex, setHoveredRowIndex] = useState(-1); - function renderTableCell(rowId: string, rowIndex: number, column: ILimitColumnDef) { + function renderTableCell(rowId: string, rowIndex: number, column: ColumnText | ColumnNumeric) { const name = `${arrayFormName}[${rowIndex}].${column.dataKey}`; return ( - {columnsDefinition.map((column: ILimitColumnDef) => ( + {columnsDefinition.map((column) => ( {column.label} diff --git a/src/components/dialogs/network-modifications/two-windings-transformer/tap-changer-pane/phase-tap-changer-pane/phase-tap-changer-pane-steps.jsx b/src/components/dialogs/network-modifications/two-windings-transformer/tap-changer-pane/phase-tap-changer-pane/phase-tap-changer-pane-steps.jsx index ceef7bbae2..907d4c3714 100644 --- a/src/components/dialogs/network-modifications/two-windings-transformer/tap-changer-pane/phase-tap-changer-pane/phase-tap-changer-pane-steps.jsx +++ b/src/components/dialogs/network-modifications/two-windings-transformer/tap-changer-pane/phase-tap-changer-pane/phase-tap-changer-pane-steps.jsx @@ -20,6 +20,7 @@ import { useIntl } from 'react-intl'; import TapChangerSteps from '../tap-changer-steps'; import { parseIntData } from '../../../../dialog-utils'; import { PHASE_TAP } from '../../creation/two-windings-transformer-creation-dialog'; +import { DndColumnType } from 'components/utils/dnd-table/dnd-table'; const PhaseTapChangerPaneSteps = ({ disabled, previousValues, editData, currentNode, isModification = false }) => { const intl = useIntl(); @@ -29,6 +30,7 @@ const PhaseTapChangerPaneSteps = ({ disabled, previousValues, editData, currentN { label: 'Tap', dataKey: STEPS_TAP, + type: DndColumnType.TEXT, }, { label: 'DeltaResistance', diff --git a/src/components/dialogs/network-modifications/two-windings-transformer/tap-changer-pane/ratio-tap-changer-pane/ratio-tap-changer-pane-steps.jsx b/src/components/dialogs/network-modifications/two-windings-transformer/tap-changer-pane/ratio-tap-changer-pane/ratio-tap-changer-pane-steps.jsx index 5b2d85da72..419f27ac3d 100644 --- a/src/components/dialogs/network-modifications/two-windings-transformer/tap-changer-pane/ratio-tap-changer-pane/ratio-tap-changer-pane-steps.jsx +++ b/src/components/dialogs/network-modifications/two-windings-transformer/tap-changer-pane/ratio-tap-changer-pane/ratio-tap-changer-pane-steps.jsx @@ -19,6 +19,7 @@ import { useIntl } from 'react-intl'; import TapChangerSteps from '../tap-changer-steps'; import { parseIntData } from '../../../../dialog-utils'; import { RATIO_TAP } from '../../creation/two-windings-transformer-creation-dialog'; +import { DndColumnType } from 'components/utils/dnd-table/dnd-table'; const RatioTapChangerPaneSteps = ({ disabled, previousValues, editData, currentNode, isModification = false }) => { const intl = useIntl(); @@ -28,13 +29,14 @@ const RatioTapChangerPaneSteps = ({ disabled, previousValues, editData, currentN { label: 'Tap', dataKey: STEPS_TAP, + type: DndColumnType.TEXT, }, { label: 'DeltaResistance', dataKey: STEPS_RESISTANCE, initialValue: 0, editable: true, - numeric: true, + type: DndColumnType.NUMERIC, clearable: false, }, { @@ -42,7 +44,7 @@ const RatioTapChangerPaneSteps = ({ disabled, previousValues, editData, currentN dataKey: STEPS_REACTANCE, initialValue: 0, editable: true, - numeric: true, + type: DndColumnType.NUMERIC, clearable: false, }, { @@ -50,7 +52,7 @@ const RatioTapChangerPaneSteps = ({ disabled, previousValues, editData, currentN dataKey: STEPS_CONDUCTANCE, initialValue: 0, editable: true, - numeric: true, + type: DndColumnType.NUMERIC, clearable: false, }, { @@ -58,7 +60,7 @@ const RatioTapChangerPaneSteps = ({ disabled, previousValues, editData, currentN dataKey: STEPS_SUSCEPTANCE, initialValue: 0, editable: true, - numeric: true, + type: DndColumnType.NUMERIC, clearable: false, }, { @@ -66,7 +68,7 @@ const RatioTapChangerPaneSteps = ({ disabled, previousValues, editData, currentN dataKey: STEPS_RATIO, initialValue: 1, editable: true, - numeric: true, + type: DndColumnType.NUMERIC, clearable: false, }, ].map((column) => ({ diff --git a/src/components/dialogs/parameters/voltageinit/voltage-limits-parameters.tsx b/src/components/dialogs/parameters/voltageinit/voltage-limits-parameters.tsx index b3de3dcdcd..499e22ca5e 100644 --- a/src/components/dialogs/parameters/voltageinit/voltage-limits-parameters.tsx +++ b/src/components/dialogs/parameters/voltageinit/voltage-limits-parameters.tsx @@ -5,7 +5,13 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -import DndTable from 'components/utils/dnd-table/dnd-table'; +import DndTable, { + ColumnDirectoryItem, + ColumnNumeric, + ColumnText, + DndColumn, + DndColumnType, +} from 'components/utils/dnd-table/dnd-table'; import { FILTERS, HIGH_VOLTAGE_LIMIT, @@ -25,9 +31,6 @@ import { VoltageAdornment } from '../../dialog-utils'; import { styles } from '../parameters'; import Alert from '@mui/material/Alert'; -// TODO: to fix when either migrating DndTable to typescript or using something else than DndTable -const DndTableTyped = DndTable as React.ComponentType; - const VoltageLimitsParameters = () => { const intl = useIntl(); const VoltageLevelFilterTooltip = useMemo(() => { @@ -44,14 +47,14 @@ const VoltageLimitsParameters = () => { ); }, [intl]); - const VOLTAGE_LIMITS_MODIFICATION_COLUMNS_DEFINITIONS = useMemo(() => { + const VOLTAGE_LIMITS_MODIFICATION_COLUMNS_DEFINITIONS: (DndColumn & { initialValue: any })[] = useMemo(() => { return [ { label: 'VoltageLevelFilter', dataKey: FILTERS, initialValue: [], editable: true, - directoryItems: true, + type: DndColumnType.DIRECTORY_ITEMS, equipmentTypes: [EQUIPMENT_TYPES.VOLTAGE_LEVEL], elementType: ElementType.FILTER, titleId: 'FiltersListsSelection', @@ -62,7 +65,7 @@ const VoltageLimitsParameters = () => { dataKey: LOW_VOLTAGE_LIMIT, initialValue: null, editable: true, - numeric: true, + type: DndColumnType.NUMERIC, adornment: VoltageAdornment, textAlign: 'right', }, @@ -71,7 +74,7 @@ const VoltageLimitsParameters = () => { dataKey: HIGH_VOLTAGE_LIMIT, initialValue: null, editable: true, - numeric: true, + type: DndColumnType.NUMERIC, adornment: VoltageAdornment, textAlign: 'right', }, @@ -84,14 +87,14 @@ const VoltageLimitsParameters = () => { })); }, [VoltageLevelFilterTooltip, intl]); - const VOLTAGE_LIMITS_DEFAULT_COLUMNS_DEFINITIONS = useMemo(() => { + const VOLTAGE_LIMITS_DEFAULT_COLUMNS_DEFINITIONS: DndColumn[] = useMemo(() => { return [ { label: 'VoltageLevelFilter', dataKey: FILTERS, initialValue: [], editable: true, - directoryItems: true, + type: DndColumnType.DIRECTORY_ITEMS, equipmentTypes: [EQUIPMENT_TYPES.VOLTAGE_LEVEL], elementType: ElementType.FILTER, titleId: 'FiltersListsSelection', @@ -102,7 +105,7 @@ const VoltageLimitsParameters = () => { dataKey: LOW_VOLTAGE_LIMIT, initialValue: null, editable: true, - numeric: true, + type: DndColumnType.NUMERIC, adornment: VoltageAdornment, textAlign: 'right', }, @@ -111,7 +114,7 @@ const VoltageLimitsParameters = () => { dataKey: HIGH_VOLTAGE_LIMIT, initialValue: null, editable: true, - numeric: true, + type: DndColumnType.NUMERIC, adornment: VoltageAdornment, textAlign: 'right', }, @@ -162,7 +165,7 @@ const VoltageLimitsParameters = () => { - { - ; - const NodeAliasTable = () => { const treeModel = useSelector((state: AppState) => state.networkModificationTreeModel); const nodeNames: string[] = useMemo( @@ -32,11 +29,12 @@ const NodeAliasTable = () => { name: `${NODES_ALIASES}`, }); - const NODES_ALIASES_COLUMNS_DEFINITIONS = useMemo(() => { + const NODES_ALIASES_COLUMNS_DEFINITIONS: DndColumn[] = useMemo(() => { return [ { label: intl.formatMessage({ id: 'spreadsheet/parameter_aliases/node_alias' }), dataKey: NODE_ALIAS, + type: DndColumnType.TEXT, editable: true, initialValue: '', showErrorMsg: true, @@ -48,7 +46,7 @@ const NodeAliasTable = () => { dataKey: NODE_NAME, initialValue: '', editable: true, - autocomplete: true, + type: DndColumnType.AUTOCOMPLETE, options: nodeNames, width: '30%', maxWidth: '30%', @@ -68,7 +66,7 @@ const NodeAliasTable = () => { }; return ( - void; + onClose: () => void; +} + +function DndTableAddRowsDialog({ open, handleAddButton, onClose }: DndTableAddRowsDialogProps) { const [rowNumber, setRowNumber] = useState(1); function handleOnClose() { @@ -51,10 +56,4 @@ function DndTableAddRowsDialog({ open, handleAddButton, onClose }) { ); } -DndTableAddRowsDialog.prototype = { - open: PropTypes.bool.isRequired, - handleAddRowsButton: PropTypes.func.isRequired, - onClose: PropTypes.func.isRequired, -}; - export default DndTableAddRowsDialog; diff --git a/src/components/utils/dnd-table/dnd-table-bottom-left-buttons.jsx b/src/components/utils/dnd-table/dnd-table-bottom-left-buttons.tsx similarity index 88% rename from src/components/utils/dnd-table/dnd-table-bottom-left-buttons.jsx rename to src/components/utils/dnd-table/dnd-table-bottom-left-buttons.tsx index ea59e73067..8fc4a6440c 100644 --- a/src/components/utils/dnd-table/dnd-table-bottom-left-buttons.jsx +++ b/src/components/utils/dnd-table/dnd-table-bottom-left-buttons.tsx @@ -13,6 +13,16 @@ import ReplayIcon from '@mui/icons-material/Replay'; import { useIntl } from 'react-intl'; import PropTypes from 'prop-types'; +interface DndTableBottomLeftButtonsProps { + handleUploadButton: () => void; + uploadButtonMessageId: string; + handleResetButton: () => void; + resetButtonMessageId: string; + withResetButton?: boolean; + disableUploadButton?: boolean; + disabled?: boolean; +} + const DndTableBottomLeftButtons = ({ handleUploadButton, uploadButtonMessageId, @@ -21,7 +31,7 @@ const DndTableBottomLeftButtons = ({ withResetButton, disableUploadButton, disabled, -}) => { +}: DndTableBottomLeftButtonsProps) => { const intl = useIntl(); return ( diff --git a/src/components/utils/dnd-table/dnd-table-bottom-right-buttons.jsx b/src/components/utils/dnd-table/dnd-table-bottom-right-buttons.tsx similarity index 91% rename from src/components/utils/dnd-table/dnd-table-bottom-right-buttons.jsx rename to src/components/utils/dnd-table/dnd-table-bottom-right-buttons.tsx index e2f2ef3cfe..717304d2f0 100644 --- a/src/components/utils/dnd-table/dnd-table-bottom-right-buttons.jsx +++ b/src/components/utils/dnd-table/dnd-table-bottom-right-buttons.tsx @@ -17,6 +17,17 @@ import { useIntl } from 'react-intl'; import PropTypes from 'prop-types'; import { SELECTED } from '../field-constants'; +export interface DndTableBottomRightButtonsProps { + arrayFormName: string; + handleAddButton: () => void; + handleDeleteButton: () => void; + handleMoveUpButton: () => void; + handleMoveDownButton: () => void; + disableAddingRows?: boolean; + showMoveArrow?: boolean; + disabled?: boolean; +} + const DndTableBottomRightButtons = ({ arrayFormName, handleAddButton, @@ -26,10 +37,10 @@ const DndTableBottomRightButtons = ({ disableAddingRows, showMoveArrow, disabled, -}) => { +}: DndTableBottomRightButtonsProps) => { const intl = useIntl(); - const currentRows = useWatch({ + const currentRows: any[] = useWatch({ name: arrayFormName, }); @@ -119,14 +130,4 @@ const DndTableBottomRightButtons = ({ ); }; -DndTableBottomRightButtons.prototype = { - arrayFormName: PropTypes.string.isRequired, - handleAddButton: PropTypes.func.isRequired, - handleDeleteButton: PropTypes.func.isRequired, - handleMoveUpButton: PropTypes.func.isRequired, - handleMoveDownButton: PropTypes.func.isRequired, - showMoveArrow: PropTypes.bool, - disabled: PropTypes.bool, -}; - export default DndTableBottomRightButtons; diff --git a/src/components/utils/dnd-table/dnd-table.jsx b/src/components/utils/dnd-table/dnd-table.tsx similarity index 71% rename from src/components/utils/dnd-table/dnd-table.jsx rename to src/components/utils/dnd-table/dnd-table.tsx index b18602f25d..020223fa6f 100644 --- a/src/components/utils/dnd-table/dnd-table.jsx +++ b/src/components/utils/dnd-table/dnd-table.tsx @@ -6,10 +6,11 @@ */ import { useMemo, useState } from 'react'; -import { useFormContext, useWatch } from 'react-hook-form'; +import { UseFieldArrayReturn, useFormContext, useWatch } from 'react-hook-form'; import { Box, Checkbox, + CheckboxProps, Grid, Table, TableBody, @@ -20,13 +21,19 @@ import { Tooltip, } from '@mui/material'; import DragIndicatorIcon from '@mui/icons-material/DragIndicator'; -import { DragDropContext, Draggable, Droppable } from 'react-beautiful-dnd'; +import { DragDropContext, Draggable, Droppable, DroppableProvided, DropResult } from 'react-beautiful-dnd'; import { useIntl } from 'react-intl'; import DndTableBottomLeftButtons from './dnd-table-bottom-left-buttons'; import DndTableBottomRightButtons from './dnd-table-bottom-right-buttons'; -import { TableNumericalInput } from '../rhf-inputs/table-inputs/table-numerical-input'; +import { TableNumericalInput, TableNumericalInputProps } from '../rhf-inputs/table-inputs/table-numerical-input'; import { TableTextInput } from '../rhf-inputs/table-inputs/table-text-input'; -import { AutocompleteInput, CheckboxInput, RawReadOnlyInput } from '@gridsuite/commons-ui'; +import { + AutocompleteInput, + CheckboxInput, + ElementType, + RawReadOnlyInput, + RawReadOnlyInputProps, +} from '@gridsuite/commons-ui'; import PropTypes from 'prop-types'; import { SELECTED } from '../field-constants'; import { ErrorInput } from '@gridsuite/commons-ui'; @@ -34,6 +41,7 @@ import { FieldErrorAlert } from '@gridsuite/commons-ui'; import DndTableAddRowsDialog from './dnd-table-add-rows-dialog'; import { DirectoryItemsInput } from '@gridsuite/commons-ui'; import ChipItemsInput from '../rhf-inputs/chip-items-input'; +import { EQUIPMENT_TYPES } from '../equipment-types'; export const MAX_ROWS_NUMBER = 100; const styles = { @@ -45,14 +53,20 @@ const styles = { textTransform: 'none', }, }; -export const addSelectedFieldToRows = (rows) => { +export const addSelectedFieldToRows = (rows: any[]) => { return rows?.map((row) => { return { ...row, [SELECTED]: false }; }); }; -function MultiCheckbox({ arrayFormName, handleClickCheck, handleClickUncheck, ...props }) { - const arrayToWatch = useWatch({ +interface MultiCheckboxProps extends Omit { + arrayFormName: string; + handleClickCheck: () => void; + handleClickUncheck: () => void; +} + +function MultiCheckbox({ arrayFormName, handleClickCheck, handleClickUncheck, ...props }: MultiCheckboxProps) { + const arrayToWatch: ({ selected: boolean } & Record)[] = useWatch({ name: arrayFormName, }); @@ -71,7 +85,60 @@ function MultiCheckbox({ arrayFormName, handleClickCheck, handleClickUncheck, .. ); } -function DefaultTableCell({ arrayFormName, rowIndex, column, ...props }) { +export enum DndColumnType { + TEXT = 'TEXT', + NUMERIC = 'NUMERIC', + AUTOCOMPLETE = 'AUTOCOMPLETE', + CHIP_ITEMS = 'CHIP_ITEMS', + DIRECTORY_ITEMS = 'DIRECTORY_ITEMS', +} + +interface ColumnBase { + dataKey: string; + maxWidth?: number | string; + width?: number | string; + label?: string; + extra?: JSX.Element; + editable?: boolean; +} + +export interface ColumnText extends ColumnBase { + type: DndColumnType.TEXT; + showErrorMsg?: boolean; +} + +export interface ColumnNumeric extends ColumnBase { + type: DndColumnType.NUMERIC; + adornment?: { text: string }; + clearable?: boolean; + textAlign?: 'right' | 'left'; +} + +export interface ColumnAutocomplete extends ColumnBase { + type: DndColumnType.AUTOCOMPLETE; + options: string[]; +} + +export interface ColumnDirectoryItem extends ColumnBase { + type: DndColumnType.DIRECTORY_ITEMS; + equipmentTypes: EQUIPMENT_TYPES[]; + elementType: ElementType; + titleId: string; +} + +export interface ColumnChipsItem extends ColumnBase { + type: DndColumnType.CHIP_ITEMS; +} + +export type DndColumn = ColumnNumeric | ColumnAutocomplete | ColumnText | ColumnDirectoryItem | ColumnChipsItem; + +interface DefaultTableCellProps { + arrayFormName: string; + rowIndex: number; + column: ColumnBase; +} + +function DefaultTableCell({ arrayFormName, rowIndex, column, ...props }: DefaultTableCellProps) { return ( @@ -79,11 +146,28 @@ function DefaultTableCell({ arrayFormName, rowIndex, column, ...props }) { ); } -function EditableTableCell({ arrayFormName, rowIndex, column, previousValue, valueModified, ...props }) { +interface EditableTableCellProps { + arrayFormName: string; + rowIndex: number; + column: DndColumn; + previousValue?: number; + valueModified: boolean; + disabled?: boolean; +} + +function EditableTableCell({ + arrayFormName, + rowIndex, + column, + previousValue, + valueModified, + ...props +}: EditableTableCellProps) { return ( - {column.numeric && ( + {column.type === DndColumnType.NUMERIC && ( )} - {!column.numeric && !column.directoryItems && !column.chipItems && !column.autocomplete && ( + {column.type === DndColumnType.TEXT && ( )} - {column.autocomplete && ( + {column.type === DndColumnType.AUTOCOMPLETE && ( )} - {column.directoryItems && ( + {column.type === DndColumnType.DIRECTORY_ITEMS && ( )} - {column.chipItems && ( + {column.type === DndColumnType.CHIP_ITEMS && ( )} ); } -const DndTable = ({ - arrayFormName, - useFieldArrayOutput, - columnsDefinition, - tableHeight, - allowedToAddRows = () => Promise.resolve(true), - createRows, - handleUploadButton, - uploadButtonMessageId, - handleResetButton, - resetButtonMessageId, - disabled = false, - withResetButton = false, - withLeftButtons = true, - withAddRowsDialog = true, - previousValues, - disableTableCell, - getPreviousValue, - isValueModified, - disableAddingRows = false, - showMoveArrow = true, - disableDragAndDrop = false, -}) => { +interface DndTableBaseProps { + arrayFormName: string; + useFieldArrayOutput: UseFieldArrayReturn; + columnsDefinition: DndColumn[]; + tableHeight?: number; + allowedToAddRows?: () => Promise; + createRows?: (numberOfRows: number) => { + [key: string]: any; + }[]; + disabled?: boolean; + withResetButton?: boolean; + withLeftButtons?: boolean; + withAddRowsDialog?: boolean; + previousValues?: any[]; + disableTableCell?: (rowIndex: number, column: any, arrayFormName: string, temporaryLimits?: any[]) => boolean; + getPreviousValue?: (rowIndex: number, column: any, arrayFormName: string, temporaryLimits?: any[]) => number; + isValueModified?: (index: number, arrayFormName: string) => boolean; + disableAddingRows?: boolean; + showMoveArrow?: boolean; + disableDragAndDrop?: boolean; +} + +interface DndTableWithLeftButtonsProps extends DndTableBaseProps { + withLeftButtons?: true; + handleUploadButton: () => void; + uploadButtonMessageId: string; + handleResetButton: () => void; + resetButtonMessageId: string; +} + +interface DndTableWithoutLeftButtonsProps extends DndTableBaseProps { + withLeftButtons: false; +} + +type DndTableProps = DndTableWithLeftButtonsProps | DndTableWithoutLeftButtonsProps; + +const DndTable = (props: DndTableProps) => { + const { + arrayFormName, + useFieldArrayOutput, + columnsDefinition, + tableHeight, + allowedToAddRows = () => Promise.resolve(true), + createRows, + disabled = false, + withResetButton = false, + withAddRowsDialog = true, + previousValues, + disableTableCell, + getPreviousValue, + isValueModified, + disableAddingRows = false, + showMoveArrow = true, + disableDragAndDrop = false, + } = props; const intl = useIntl(); const { getValues, setValue, setError, clearErrors } = useFormContext(); @@ -166,7 +282,7 @@ const DndTable = ({ const [openAddRowsDialog, setOpenAddRowsDialog] = useState(false); - function renderTableCell(rowId, rowIndex, column) { + function renderTableCell(rowId: string, rowIndex: number, column: DndColumn) { let CustomTableCell = column.editable ? EditableTableCell : DefaultTableCell; return ( MAX_ROWS_NUMBER) { setError(arrayFormName, { type: 'custom', - message: { - id: 'MaximumRowNumberError', - value: MAX_ROWS_NUMBER, - }, + message: intl.formatMessage( + { + id: 'MaximumRowNumberError', + }, + { + value: MAX_ROWS_NUMBER, + } + ), }); return; } clearErrors(arrayFormName); - const rowsToAdd = createRows(numberOfRows).map((row) => { + const rowsToAdd = createRows?.(numberOfRows).map((row) => { return { ...row, [SELECTED]: false }; }); @@ -279,7 +399,7 @@ const DndTable = ({ } } - function onDragEnd(result) { + function onDragEnd(result: DropResult) { // dropped outside the list if (!result.destination) { return; @@ -316,7 +436,7 @@ const DndTable = ({ ); } - function renderTableBody(providedDroppable) { + function renderTableBody(providedDroppable: DroppableProvided) { return ( {currentRows.map((row, index) => ( @@ -384,17 +504,17 @@ const DndTable = ({ - {withLeftButtons && ( + {props.withLeftButtons === undefined || props.withLeftButtons ? ( - )} + ) : null} { From f6b845231b1ccf3f72ffd3cc62fd4284680ba3f7 Mon Sep 17 00:00:00 2001 From: LE SAULNIER Kevin Date: Mon, 24 Feb 2025 10:33:52 +0100 Subject: [PATCH 3/6] fix: keep fixing TS problems Signed-off-by: LE SAULNIER Kevin --- src/components/diagrams/diagram-pane.tsx | 6 ++--- .../network-area-diagram-content.tsx | 2 +- .../commons/modification-dialog-content.jsx | 2 +- .../dialogs/limits/limits-side-pane.tsx | 18 ++++++++++----- .../converter-station-utils.tsx | 3 ++- .../phase-tap-changer-pane-steps.jsx | 12 +++++----- .../voltageinit/voltage-limits-parameters.tsx | 22 +++++++++---------- .../custom-columns/node-alias-table.tsx | 2 +- src/components/utils/dnd-table/dnd-table.tsx | 7 +++++- src/components/utils/equipmentInfosHandler.ts | 2 +- .../table-inputs/table-numerical-input.tsx | 2 +- src/components/utils/utils.ts | 9 ++++---- src/services/network-modification-types.ts | 2 +- 13 files changed, 51 insertions(+), 38 deletions(-) diff --git a/src/components/diagrams/diagram-pane.tsx b/src/components/diagrams/diagram-pane.tsx index 2c267dd7c9..44838471cb 100644 --- a/src/components/diagrams/diagram-pane.tsx +++ b/src/components/diagrams/diagram-pane.tsx @@ -188,7 +188,7 @@ const useDisplayView = (studyUuid: UUID, currentNode: CurrentTreeNode, currentRo function createSubstationDiagramView(id: UUID, state: ViewState | undefined) { const svgUrl = checkAndGetSubstationSingleLineDiagramUrl(id); return fetchSvgData(svgUrl, DiagramType.SUBSTATION).then((svg) => { - let label = getNameOrId(svg.additionalMetadata) ?? id; + let label = getNameOrId(svg.additionalMetadata as any) ?? id; //TODO to fix return { id: id, nodeId: currentNode.id, @@ -205,7 +205,7 @@ const useDisplayView = (studyUuid: UUID, currentNode: CurrentTreeNode, currentRo function createVoltageLevelDiagramView(id: UUID, state: ViewState | undefined) { const svgUrl = checkAndGetVoltageLevelSingleLineDiagramUrl(id); return fetchSvgData(svgUrl, DiagramType.VOLTAGE_LEVEL).then((svg) => { - let label = getNameOrId(svg.additionalMetadata) ?? id; + let label = getNameOrId(svg.additionalMetadata as any) ?? id; //TODO to fix let substationId = svg.additionalMetadata?.substationId; return { id: id, @@ -1017,7 +1017,7 @@ export function DiagramPane({ const getDiagramTitle = (diagramView: DiagramView) => { return diagramView.svgType !== DiagramType.NETWORK_AREA_DIAGRAM - ? diagramView.name + ' - ' + translate(diagramView.country) + ? diagramView.name + ' - ' + (diagramView.country ? translate(diagramView.country) : '') : diagramView.name; }; diff --git a/src/components/diagrams/networkAreaDiagram/network-area-diagram-content.tsx b/src/components/diagrams/networkAreaDiagram/network-area-diagram-content.tsx index 331fa20d7e..c8a91dbc3e 100644 --- a/src/components/diagrams/networkAreaDiagram/network-area-diagram-content.tsx +++ b/src/components/diagrams/networkAreaDiagram/network-area-diagram-content.tsx @@ -341,7 +341,7 @@ function NetworkAreaDiagramContent(props: NetworkAreaDiagramContentProps) { sx={mergeSx( styles.divDiagram, styles.divNetworkAreaDiagram, - loadFlowStatus !== RunningStatus.SUCCEED && styles.divDiagramInvalid + loadFlowStatus !== RunningStatus.SUCCEED ? styles.divDiagramInvalid : undefined )} /> diff --git a/src/components/dialogs/commons/modification-dialog-content.jsx b/src/components/dialogs/commons/modification-dialog-content.jsx index 732ff2c987..1ac599db5f 100644 --- a/src/components/dialogs/commons/modification-dialog-content.jsx +++ b/src/components/dialogs/commons/modification-dialog-content.jsx @@ -8,7 +8,7 @@ import { FormattedMessage } from 'react-intl'; import { Grid, Dialog, DialogTitle, DialogContent, DialogActions, LinearProgress } from '@mui/material'; import PropTypes from 'prop-types'; -import { useButtonWithTooltip } from '../../utils/inputs/input-hooks.tsx'; +import { useButtonWithTooltip } from '../../utils/inputs/input-hooks'; import FindInPageIcon from '@mui/icons-material/FindInPage'; import AutoStoriesOutlinedIcon from '@mui/icons-material/AutoStoriesOutlined'; import { useSelector } from 'react-redux'; diff --git a/src/components/dialogs/limits/limits-side-pane.tsx b/src/components/dialogs/limits/limits-side-pane.tsx index 804fdcbdd9..7903d6d459 100644 --- a/src/components/dialogs/limits/limits-side-pane.tsx +++ b/src/components/dialogs/limits/limits-side-pane.tsx @@ -65,7 +65,7 @@ export function LimitsSidePane({ dataKey: TEMPORARY_LIMIT_NAME, initialValue: '', editable: true, - type: DndColumnType.TEXT, + type: DndColumnType.TEXT as DndColumnType.TEXT, maxWidth: 200, }, { @@ -73,7 +73,7 @@ export function LimitsSidePane({ dataKey: TEMPORARY_LIMIT_DURATION, initialValue: null, editable: true, - type: DndColumnType.NUMERIC, + type: DndColumnType.NUMERIC as DndColumnType.NUMERIC, maxWidth: 100, }, { @@ -81,7 +81,7 @@ export function LimitsSidePane({ dataKey: TEMPORARY_LIMIT_VALUE, initialValue: null, editable: true, - type: DndColumnType.NUMERIC, + type: DndColumnType.NUMERIC as DndColumnType.NUMERIC, maxWidth: 100, }, ].map((column) => ({ @@ -99,6 +99,9 @@ export function LimitsSidePane({ const temporaryLimitHasPreviousValue = useCallback( (rowIndex: number, arrayFormName: string, temporaryLimits?: TemporaryLimit[]) => { + if (!temporaryLimits) { + return false; + } return ( formatTemporaryLimits(temporaryLimits)?.filter( (l) => @@ -111,7 +114,7 @@ export function LimitsSidePane({ ); const shouldReturnPreviousValue = useCallback( - (rowIndex: number, column: ILimitColumnDef, arrayFormName: string, temporaryLimits?: TemporaryLimit[]) => { + (rowIndex: number, column: DndColumn, arrayFormName: string, temporaryLimits: TemporaryLimit[]) => { return ( (temporaryLimitHasPreviousValue(rowIndex, arrayFormName, temporaryLimits) && column.dataKey === TEMPORARY_LIMIT_VALUE) || @@ -133,7 +136,10 @@ export function LimitsSidePane({ ); const getPreviousValue = useCallback( - (rowIndex: number, column: ILimitColumnDef, arrayFormName: string, temporaryLimits: TemporaryLimit[]) => { + (rowIndex: number, column: DndColumn, arrayFormName: string, temporaryLimits?: TemporaryLimit[]) => { + if (!temporaryLimits) { + return undefined; + } const formattedTemporaryLimits = formatTemporaryLimits(temporaryLimits); if (!temporaryLimits?.length) { return undefined; @@ -155,7 +161,7 @@ export function LimitsSidePane({ ); const disableTableCell = useCallback( - (rowIndex: number, column: ILimitColumnDef, arrayFormName: string, temporaryLimits: TemporaryLimit[]) => { + (rowIndex: number, column: DndColumn, arrayFormName: string, temporaryLimits?: TemporaryLimit[]) => { // If the temporary limit is added, all fields are editable // otherwise, only the value field is editable return getValues(arrayFormName)[rowIndex]?.modificationType === TEMPORARY_LIMIT_MODIFICATION_TYPE.ADDED diff --git a/src/components/dialogs/network-modifications/hvdc-line/vsc/converter-station/converter-station-utils.tsx b/src/components/dialogs/network-modifications/hvdc-line/vsc/converter-station/converter-station-utils.tsx index 6270d01d7b..8ed304842a 100644 --- a/src/components/dialogs/network-modifications/hvdc-line/vsc/converter-station/converter-station-utils.tsx +++ b/src/components/dialogs/network-modifications/hvdc-line/vsc/converter-station/converter-station-utils.tsx @@ -41,6 +41,7 @@ import { import { UNDEFINED_CONNECTION_DIRECTION } from '../../../../../network/constants'; import { sanitizeString } from '../../../../dialog-utils'; import { toModificationOperation } from '../../../../../utils/utils'; +import { VSCModificationConverterStation } from 'services/network-modification-types'; export type UpdateReactiveCapabilityCurveTable = (action: string, index: number) => void; @@ -88,7 +89,7 @@ export interface ConverterStationInterfaceEditData { } export interface AttributeModification { - value: T; + value?: T; op: string; } diff --git a/src/components/dialogs/network-modifications/two-windings-transformer/tap-changer-pane/phase-tap-changer-pane/phase-tap-changer-pane-steps.jsx b/src/components/dialogs/network-modifications/two-windings-transformer/tap-changer-pane/phase-tap-changer-pane/phase-tap-changer-pane-steps.jsx index 907d4c3714..fb4d130a10 100644 --- a/src/components/dialogs/network-modifications/two-windings-transformer/tap-changer-pane/phase-tap-changer-pane/phase-tap-changer-pane-steps.jsx +++ b/src/components/dialogs/network-modifications/two-windings-transformer/tap-changer-pane/phase-tap-changer-pane/phase-tap-changer-pane-steps.jsx @@ -37,7 +37,7 @@ const PhaseTapChangerPaneSteps = ({ disabled, previousValues, editData, currentN dataKey: STEPS_RESISTANCE, initialValue: 0, editable: true, - numeric: true, + type: DndColumnType.NUMERIC, clearable: false, }, { @@ -45,7 +45,7 @@ const PhaseTapChangerPaneSteps = ({ disabled, previousValues, editData, currentN dataKey: STEPS_REACTANCE, initialValue: 0, editable: true, - numeric: true, + type: DndColumnType.NUMERIC, clearable: false, }, { @@ -53,7 +53,7 @@ const PhaseTapChangerPaneSteps = ({ disabled, previousValues, editData, currentN dataKey: STEPS_CONDUCTANCE, initialValue: 0, editable: true, - numeric: true, + type: DndColumnType.NUMERIC, clearable: false, }, { @@ -61,7 +61,7 @@ const PhaseTapChangerPaneSteps = ({ disabled, previousValues, editData, currentN dataKey: STEPS_SUSCEPTANCE, initialValue: 0, editable: true, - numeric: true, + type: DndColumnType.NUMERIC, clearable: false, }, { @@ -69,7 +69,7 @@ const PhaseTapChangerPaneSteps = ({ disabled, previousValues, editData, currentN dataKey: STEPS_RATIO, initialValue: 1, editable: true, - numeric: true, + type: DndColumnType.NUMERIC, clearable: false, }, { @@ -77,7 +77,7 @@ const PhaseTapChangerPaneSteps = ({ disabled, previousValues, editData, currentN dataKey: STEPS_ALPHA, initialValue: 0, editable: true, - numeric: true, + type: DndColumnType.NUMERIC, clearable: false, }, ].map((column) => ({ diff --git a/src/components/dialogs/parameters/voltageinit/voltage-limits-parameters.tsx b/src/components/dialogs/parameters/voltageinit/voltage-limits-parameters.tsx index 499e22ca5e..d974ef0ba2 100644 --- a/src/components/dialogs/parameters/voltageinit/voltage-limits-parameters.tsx +++ b/src/components/dialogs/parameters/voltageinit/voltage-limits-parameters.tsx @@ -54,7 +54,7 @@ const VoltageLimitsParameters = () => { dataKey: FILTERS, initialValue: [], editable: true, - type: DndColumnType.DIRECTORY_ITEMS, + type: DndColumnType.DIRECTORY_ITEMS as DndColumnType.DIRECTORY_ITEMS, //TODO: ??? equipmentTypes: [EQUIPMENT_TYPES.VOLTAGE_LEVEL], elementType: ElementType.FILTER, titleId: 'FiltersListsSelection', @@ -65,18 +65,18 @@ const VoltageLimitsParameters = () => { dataKey: LOW_VOLTAGE_LIMIT, initialValue: null, editable: true, - type: DndColumnType.NUMERIC, + type: DndColumnType.NUMERIC as DndColumnType.NUMERIC, adornment: VoltageAdornment, - textAlign: 'right', + textAlign: 'right' as 'right' | 'left', }, { label: 'HighVoltageLimitAdjustment', dataKey: HIGH_VOLTAGE_LIMIT, initialValue: null, editable: true, - type: DndColumnType.NUMERIC, + type: DndColumnType.NUMERIC as DndColumnType.NUMERIC, adornment: VoltageAdornment, - textAlign: 'right', + textAlign: 'right' as 'right' | 'left', }, ].map((column) => ({ ...column, @@ -87,14 +87,14 @@ const VoltageLimitsParameters = () => { })); }, [VoltageLevelFilterTooltip, intl]); - const VOLTAGE_LIMITS_DEFAULT_COLUMNS_DEFINITIONS: DndColumn[] = useMemo(() => { + const VOLTAGE_LIMITS_DEFAULT_COLUMNS_DEFINITIONS: (DndColumn & { initialValue: any[] | null })[] = useMemo(() => { return [ { label: 'VoltageLevelFilter', dataKey: FILTERS, initialValue: [], editable: true, - type: DndColumnType.DIRECTORY_ITEMS, + type: DndColumnType.DIRECTORY_ITEMS as DndColumnType.DIRECTORY_ITEMS, //TODO: ??? equipmentTypes: [EQUIPMENT_TYPES.VOLTAGE_LEVEL], elementType: ElementType.FILTER, titleId: 'FiltersListsSelection', @@ -105,18 +105,18 @@ const VoltageLimitsParameters = () => { dataKey: LOW_VOLTAGE_LIMIT, initialValue: null, editable: true, - type: DndColumnType.NUMERIC, + type: DndColumnType.NUMERIC as DndColumnType.NUMERIC, adornment: VoltageAdornment, - textAlign: 'right', + textAlign: 'right' as 'right' | 'left', //TODO ??? }, { label: 'HighVoltageLimitDefault', dataKey: HIGH_VOLTAGE_LIMIT, initialValue: null, editable: true, - type: DndColumnType.NUMERIC, + type: DndColumnType.NUMERIC as DndColumnType.NUMERIC, adornment: VoltageAdornment, - textAlign: 'right', + textAlign: 'right' as 'right' | 'left', }, ].map((column) => ({ ...column, diff --git a/src/components/spreadsheet/custom-columns/node-alias-table.tsx b/src/components/spreadsheet/custom-columns/node-alias-table.tsx index 5ec5f7aee2..cab7159ab6 100644 --- a/src/components/spreadsheet/custom-columns/node-alias-table.tsx +++ b/src/components/spreadsheet/custom-columns/node-alias-table.tsx @@ -29,7 +29,7 @@ const NodeAliasTable = () => { name: `${NODES_ALIASES}`, }); - const NODES_ALIASES_COLUMNS_DEFINITIONS: DndColumn[] = useMemo(() => { + const NODES_ALIASES_COLUMNS_DEFINITIONS: (DndColumn & { initialValue?: string })[] = useMemo(() => { return [ { label: intl.formatMessage({ id: 'spreadsheet/parameter_aliases/node_alias' }), diff --git a/src/components/utils/dnd-table/dnd-table.tsx b/src/components/utils/dnd-table/dnd-table.tsx index 020223fa6f..4b36b755d4 100644 --- a/src/components/utils/dnd-table/dnd-table.tsx +++ b/src/components/utils/dnd-table/dnd-table.tsx @@ -228,7 +228,12 @@ interface DndTableBaseProps { withAddRowsDialog?: boolean; previousValues?: any[]; disableTableCell?: (rowIndex: number, column: any, arrayFormName: string, temporaryLimits?: any[]) => boolean; - getPreviousValue?: (rowIndex: number, column: any, arrayFormName: string, temporaryLimits?: any[]) => number; + getPreviousValue?: ( + rowIndex: number, + column: any, + arrayFormName: string, + temporaryLimits?: any[] + ) => number | undefined; isValueModified?: (index: number, arrayFormName: string) => boolean; disableAddingRows?: boolean; showMoveArrow?: boolean; diff --git a/src/components/utils/equipmentInfosHandler.ts b/src/components/utils/equipmentInfosHandler.ts index fe54e6b5fa..b8b365839e 100644 --- a/src/components/utils/equipmentInfosHandler.ts +++ b/src/components/utils/equipmentInfosHandler.ts @@ -14,7 +14,7 @@ import { Identifiable } from '@gridsuite/commons-ui'; export const useNameOrId = () => { const useName = useSelector((state: AppState) => state[PARAM_USE_NAME]); const getNameOrId = useCallback( - (infos?: Identifiable) => { + (infos?: Identifiable | null) => { if (infos) { const name = infos.name; return useName && name != null && name.trim() !== '' ? name : infos?.id; diff --git a/src/components/utils/rhf-inputs/table-inputs/table-numerical-input.tsx b/src/components/utils/rhf-inputs/table-inputs/table-numerical-input.tsx index 810c570846..38a20badb7 100644 --- a/src/components/utils/rhf-inputs/table-inputs/table-numerical-input.tsx +++ b/src/components/utils/rhf-inputs/table-inputs/table-numerical-input.tsx @@ -61,7 +61,7 @@ export const TableNumericalInput = ({ [isClearable, previousValue, value] ); - const outputTransform = (value: string | number) => { + const outputTransform = (value?: string | number) => { if (typeof value === 'string') { if (value === '-') { return value; diff --git a/src/components/utils/utils.ts b/src/components/utils/utils.ts index 3d063e541a..597777269c 100644 --- a/src/components/utils/utils.ts +++ b/src/components/utils/utils.ts @@ -12,6 +12,7 @@ import { CURRENT_LIMITS, ID } from './field-constants'; import { addSelectedFieldToRows } from './dnd-table/dnd-table'; import { CurrentLimits, OperationalLimitsGroup, TemporaryLimit } from 'services/network-modification-types'; import { VoltageLevel } from './equipment-types'; +import { AttributeModification } from 'components/dialogs/network-modifications/hvdc-line/vsc/converter-station/converter-station-utils'; export const UNDEFINED_ACCEPTABLE_DURATION = Math.pow(2, 31) - 1; @@ -108,18 +109,18 @@ export const buildNewBusbarSections = (equipmentId: string, sectionCount: number return newBusbarSections; }; -export function toModificationOperation(value: unknown) { +export function toModificationOperation(value: T): AttributeModification | null { return value === 0 || value === false || value ? { value: value, op: 'SET' } : null; } -export function toModificationUnsetOperation(value: unknown) { +export function toModificationUnsetOperation(value: T): AttributeModification | null { if (value === null) { return null; } return value === 0 || value === false || value ? { value: value, op: 'SET' } : { op: 'UNSET' }; } -export const formatTemporaryLimits = (temporaryLimits?: TemporaryLimit[]) => +export const formatTemporaryLimits = (temporaryLimits: TemporaryLimit[]) => temporaryLimits?.map((limit) => { return { name: limit?.name ?? '', @@ -139,7 +140,7 @@ export const formatCompleteCurrentLimit = (completeLimitsGroups: OperationalLimi [CURRENT_LIMITS]: { permanentLimit: elt.currentLimits.permanentLimit, //TODO: check change is ok ? temporaryLimits: addSelectedFieldToRows( - formatTemporaryLimits(elt?.currentLimits.temporaryLimits) + formatTemporaryLimits(elt.currentLimits.temporaryLimits) ), }, }); diff --git a/src/services/network-modification-types.ts b/src/services/network-modification-types.ts index 2d502c0922..1d43cdb82b 100644 --- a/src/services/network-modification-types.ts +++ b/src/services/network-modification-types.ts @@ -261,7 +261,7 @@ export interface TemporaryLimit { value: number | null; acceptableDuration: number | null; modificationType: string | null; - selected: boolean; + selected?: boolean; name: string; } From d452cf9606d495109aef7c869db12d4c1f0c1f71 Mon Sep 17 00:00:00 2001 From: LE SAULNIER Kevin Date: Mon, 24 Feb 2025 17:06:08 +0100 Subject: [PATCH 4/6] fix: typing Signed-off-by: LE SAULNIER Kevin --- src/components/diagrams/diagram-common.ts | 41 +++++++--- src/components/diagrams/diagram-pane.tsx | 38 +++++---- .../singleLineDiagram/position-diagram.tsx | 4 +- .../dialogs/limits/limits-side-pane.tsx | 6 +- .../dialogs/limits/temporary-limits-table.tsx | 3 +- .../frequency-reserve-pane.jsx | 7 +- .../substations-generators-ordering-pane.jsx | 5 +- .../converter-station-utils.tsx | 1 - .../line/creation/line-creation-dialog.jsx | 2 +- .../voltageinit/voltage-limits-parameters.tsx | 82 +++++++++---------- src/components/study-drawer.jsx | 2 +- src/components/utils/custom-nested-menu.tsx | 4 +- .../dnd-table-bottom-right-buttons.tsx | 1 - src/components/utils/dnd-table/dnd-table.tsx | 11 +-- src/components/utils/inputs/input-hooks.tsx | 2 +- .../utils/rhf-inputs/enum-input.tsx | 14 +++- .../read-only/button-read-only-input.tsx | 2 +- .../table-inputs/table-numerical-input.tsx | 9 +- src/components/utils/utils.ts | 12 ++- src/components/utils/validation-functions.ts | 30 +++++-- src/services/study/index.ts | 3 +- 21 files changed, 151 insertions(+), 128 deletions(-) diff --git a/src/components/diagrams/diagram-common.ts b/src/components/diagrams/diagram-common.ts index 1072b734fd..4afa21f51d 100644 --- a/src/components/diagrams/diagram-common.ts +++ b/src/components/diagrams/diagram-common.ts @@ -297,22 +297,41 @@ export const useDiagram = () => { }; }; -export interface Svg { +export interface SldAdditionalMetadata { + id: string; + country: string; + substationId?: string; +} + +export interface SldSvg { + svg: string | null; + metadata: SLDMetadata | null; + additionalMetadata: SldAdditionalMetadata | null; + error?: string | null; + svgUrl?: string | null; +} + +export interface DiagramAdditionalMetadata { + nbVoltageLevels: number; + scalingFactor: number; + voltageLevels: { + id: string; + substationId: UUID; + country: string; + name: string; + }[]; +} + +export interface DiagramSvg { svg: string | null; - metadata: SLDMetadata | DiagramMetadata | null; - additionalMetadata: - | (SLDMetadata & { - country: string; - substationId?: string; - voltageLevels: { name: string; substationId: UUID }[]; - nbVoltageLevels?: number; - scalingFactor?: number; - }) - | null; + metadata: DiagramMetadata | null; + additionalMetadata: DiagramAdditionalMetadata | null; error?: string | null; svgUrl?: string | null; } +export type Svg = DiagramSvg | SldSvg; + export const NoSvg: Svg = { svg: null, metadata: null, diff --git a/src/components/diagrams/diagram-pane.tsx b/src/components/diagrams/diagram-pane.tsx index 44838471cb..316bbfaa98 100644 --- a/src/components/diagrams/diagram-pane.tsx +++ b/src/components/diagrams/diagram-pane.tsx @@ -19,10 +19,13 @@ import { DEFAULT_WIDTH_SUBSTATION, DEFAULT_WIDTH_VOLTAGE_LEVEL, DIAGRAM_MAP_RATIO_MIN_PERCENTAGE, + DiagramAdditionalMetadata, + DiagramSvg, DiagramType, MAP_BOTTOM_OFFSET, NETWORK_AREA_DIAGRAM_NB_MAX_VOLTAGE_LEVELS, NoSvg, + SldSvg, Svg, useDiagram, ViewState, @@ -130,18 +133,19 @@ const useDisplayView = (studyUuid: UUID, currentNode: CurrentTreeNode, currentRo ] ); + type FetchSvgDataFn = { + (svgUrl: string | null, svgType: DiagramType.SUBSTATION | DiagramType.VOLTAGE_LEVEL): Promise; + (svgUrl: string | null, svgType: DiagramType.NETWORK_AREA_DIAGRAM): Promise; + }; // this callback returns a promise - const fetchSvgData = useCallback( - (svgUrl: string | null, svgType: DiagramType): Promise => { + const fetchSvgData = useCallback( + (svgUrl, svgType): any => { if (svgUrl) { - const fetchSvgPromise: Promise = fetchSvg(svgUrl); - return fetchSvgPromise + return fetchSvg(svgUrl) .then((data: Svg | null) => { if (data !== null) { return { - svg: data.svg, - metadata: data.metadata, - additionalMetadata: data.additionalMetadata, + ...data, error: undefined, }; } else { @@ -188,7 +192,7 @@ const useDisplayView = (studyUuid: UUID, currentNode: CurrentTreeNode, currentRo function createSubstationDiagramView(id: UUID, state: ViewState | undefined) { const svgUrl = checkAndGetSubstationSingleLineDiagramUrl(id); return fetchSvgData(svgUrl, DiagramType.SUBSTATION).then((svg) => { - let label = getNameOrId(svg.additionalMetadata as any) ?? id; //TODO to fix + let label = getNameOrId(svg.additionalMetadata) ?? id; return { id: id, nodeId: currentNode.id, @@ -205,7 +209,7 @@ const useDisplayView = (studyUuid: UUID, currentNode: CurrentTreeNode, currentRo function createVoltageLevelDiagramView(id: UUID, state: ViewState | undefined) { const svgUrl = checkAndGetVoltageLevelSingleLineDiagramUrl(id); return fetchSvgData(svgUrl, DiagramType.VOLTAGE_LEVEL).then((svg) => { - let label = getNameOrId(svg.additionalMetadata as any) ?? id; //TODO to fix + let label = getNameOrId(svg.additionalMetadata); let substationId = svg.additionalMetadata?.substationId; return { id: id, @@ -225,21 +229,18 @@ const useDisplayView = (studyUuid: UUID, currentNode: CurrentTreeNode, currentRo console.log('debug', 'createNetworkAreaDiagramView', state); if (ids?.length) { const svgUrl = checkAndGetNetworkAreaDiagramUrl(ids, depth); - return fetchSvgData(svgUrl, DiagramType.NETWORK_AREA_DIAGRAM).then((svg) => { + return fetchSvgData(svgUrl, DiagramType.NETWORK_AREA_DIAGRAM).then((svg: DiagramSvg) => { let nadTitle = ''; let substationsIds: UUID[] = []; svg.additionalMetadata?.voltageLevels - .map((vl: { name: string; substationId: UUID }) => ({ + .map((vl) => ({ name: getNameOrId({ name: vl.name, id: vl.substationId }), substationId: vl.substationId, })) .sort( - ( - vlA: { name?: string; substationId: UUID }, - vlB: { name?: string; substationId: UUID } - ) => vlA.name?.toLowerCase().localeCompare(vlB.name?.toLowerCase() ?? '') || 0 + (vlA, vlB) => vlA.name?.toLowerCase().localeCompare(vlB.name?.toLowerCase() ?? '') || 0 ) - .forEach((voltageLevel: { name?: string; substationId: UUID }) => { + .forEach((voltageLevel) => { const name = voltageLevel.name; if (name !== null) { nadTitle += (nadTitle !== '' ? ', ' : '') + name; @@ -259,7 +260,7 @@ const useDisplayView = (studyUuid: UUID, currentNode: CurrentTreeNode, currentRo svgType: DiagramType.NETWORK_AREA_DIAGRAM, depth: depth, substationIds: substationsIds, - nadMetadata: svg.metadata as DiagramMetadata, + nadMetadata: svg.metadata, scalingFactor: svg.additionalMetadata?.scalingFactor, ...svg, }; @@ -539,7 +540,8 @@ export function DiagramPane({ } as unknown as DiagramView; dispatch( setNetworkAreaDiagramNbVoltageLevels( - networkAreaDiagramView.additionalMetadata?.nbVoltageLevels ?? 0 + (networkAreaDiagramView.additionalMetadata as DiagramAdditionalMetadata) + ?.nbVoltageLevels ?? 0 ) ); return updatedViews; diff --git a/src/components/diagrams/singleLineDiagram/position-diagram.tsx b/src/components/diagrams/singleLineDiagram/position-diagram.tsx index 007f27e9e9..5b005e269c 100644 --- a/src/components/diagrams/singleLineDiagram/position-diagram.tsx +++ b/src/components/diagrams/singleLineDiagram/position-diagram.tsx @@ -60,9 +60,7 @@ const PositionDiagram = forwardRef((props: PositionDiagramProps, ref: Ref { if (data !== null) { setSvg({ - svg: data.svg, - metadata: data.metadata, - additionalMetadata: data.additionalMetadata, + ...data, error: null, svgUrl: props.svgUrl, }); diff --git a/src/components/dialogs/limits/limits-side-pane.tsx b/src/components/dialogs/limits/limits-side-pane.tsx index 7903d6d459..3a519b5ea7 100644 --- a/src/components/dialogs/limits/limits-side-pane.tsx +++ b/src/components/dialogs/limits/limits-side-pane.tsx @@ -65,7 +65,7 @@ export function LimitsSidePane({ dataKey: TEMPORARY_LIMIT_NAME, initialValue: '', editable: true, - type: DndColumnType.TEXT as DndColumnType.TEXT, + type: DndColumnType.TEXT as const, maxWidth: 200, }, { @@ -73,7 +73,7 @@ export function LimitsSidePane({ dataKey: TEMPORARY_LIMIT_DURATION, initialValue: null, editable: true, - type: DndColumnType.NUMERIC as DndColumnType.NUMERIC, + type: DndColumnType.NUMERIC as const, maxWidth: 100, }, { @@ -81,7 +81,7 @@ export function LimitsSidePane({ dataKey: TEMPORARY_LIMIT_VALUE, initialValue: null, editable: true, - type: DndColumnType.NUMERIC as DndColumnType.NUMERIC, + type: DndColumnType.NUMERIC as const, maxWidth: 100, }, ].map((column) => ({ diff --git a/src/components/dialogs/limits/temporary-limits-table.tsx b/src/components/dialogs/limits/temporary-limits-table.tsx index a3399c4802..e304fa6259 100644 --- a/src/components/dialogs/limits/temporary-limits-table.tsx +++ b/src/components/dialogs/limits/temporary-limits-table.tsx @@ -15,9 +15,8 @@ import DeleteIcon from '@mui/icons-material/Delete'; import { SELECTED } from '../../utils/field-constants'; import { TableNumericalInput } from '../../utils/rhf-inputs/table-inputs/table-numerical-input'; import { TableTextInput } from '../../utils/rhf-inputs/table-inputs/table-text-input'; -import { ILimitColumnDef } from './limits-side-pane'; import { TemporaryLimit } from '../../../services/network-modification-types'; -import { ColumnNumeric, ColumnText, DndColumn, DndColumnType } from 'components/utils/dnd-table/dnd-table'; +import { ColumnNumeric, ColumnText, DndColumnType } from 'components/utils/dnd-table/dnd-table'; const styles = { columnsStyle: { diff --git a/src/components/dialogs/network-modifications/generation-dispatch/frequency-reserve-pane.jsx b/src/components/dialogs/network-modifications/generation-dispatch/frequency-reserve-pane.jsx index 4a8910fb15..892888cdf0 100644 --- a/src/components/dialogs/network-modifications/generation-dispatch/frequency-reserve-pane.jsx +++ b/src/components/dialogs/network-modifications/generation-dispatch/frequency-reserve-pane.jsx @@ -9,7 +9,7 @@ import { FREQUENCY_RESERVE, GENERATORS_FILTERS, GENERATORS_FREQUENCY_RESERVES } import { useIntl } from 'react-intl'; import { useMemo } from 'react'; import { useFieldArray } from 'react-hook-form'; -import DndTable from 'components/utils/dnd-table/dnd-table'; +import DndTable, { DndColumnType } from 'components/utils/dnd-table/dnd-table'; import { EQUIPMENT_TYPES } from 'components/utils/equipment-types'; import { ElementType } from '@gridsuite/commons-ui'; import { Tooltip, IconButton } from '@mui/material'; @@ -25,8 +25,7 @@ const FrequencyReservePane = ({ id = GENERATORS_FREQUENCY_RESERVES }) => { dataKey: GENERATORS_FILTERS, initialValue: [], editable: true, - numeric: false, - directoryItems: true, + type: DndColumnType.DIRECTORY_ITEMS, equipmentTypes: [EQUIPMENT_TYPES.GENERATOR], elementType: ElementType.FILTER, titleId: 'FiltersListsSelection', @@ -36,7 +35,7 @@ const FrequencyReservePane = ({ id = GENERATORS_FREQUENCY_RESERVES }) => { dataKey: FREQUENCY_RESERVE, initialValue: null, editable: true, - numeric: true, + type: DndColumnType.NUMERIC, }, ].map((column) => ({ ...column, diff --git a/src/components/dialogs/network-modifications/generation-dispatch/substations-generators-ordering-pane.jsx b/src/components/dialogs/network-modifications/generation-dispatch/substations-generators-ordering-pane.jsx index b1021d96ec..935d1824ae 100644 --- a/src/components/dialogs/network-modifications/generation-dispatch/substations-generators-ordering-pane.jsx +++ b/src/components/dialogs/network-modifications/generation-dispatch/substations-generators-ordering-pane.jsx @@ -9,7 +9,7 @@ import { SUBSTATIONS_GENERATORS_ORDERING, SUBSTATION_IDS } from 'components/util import { useIntl } from 'react-intl'; import { useMemo } from 'react'; import { useFieldArray } from 'react-hook-form'; -import DndTable from 'components/utils/dnd-table/dnd-table'; +import DndTable, { DndColumnType } from 'components/utils/dnd-table/dnd-table'; const SubstationsGeneratorsOrderingPane = ({ id = SUBSTATIONS_GENERATORS_ORDERING }) => { const intl = useIntl(); @@ -21,8 +21,7 @@ const SubstationsGeneratorsOrderingPane = ({ id = SUBSTATIONS_GENERATORS_ORDERIN dataKey: SUBSTATION_IDS, initialValue: [], editable: true, - numeric: false, - chipItems: true, + type: DndColumnType.CHIP_ITEMS, }, ].map((column) => ({ ...column, diff --git a/src/components/dialogs/network-modifications/hvdc-line/vsc/converter-station/converter-station-utils.tsx b/src/components/dialogs/network-modifications/hvdc-line/vsc/converter-station/converter-station-utils.tsx index 8ed304842a..9f3cade1db 100644 --- a/src/components/dialogs/network-modifications/hvdc-line/vsc/converter-station/converter-station-utils.tsx +++ b/src/components/dialogs/network-modifications/hvdc-line/vsc/converter-station/converter-station-utils.tsx @@ -41,7 +41,6 @@ import { import { UNDEFINED_CONNECTION_DIRECTION } from '../../../../../network/constants'; import { sanitizeString } from '../../../../dialog-utils'; import { toModificationOperation } from '../../../../../utils/utils'; -import { VSCModificationConverterStation } from 'services/network-modification-types'; export type UpdateReactiveCapabilityCurveTable = (action: string, index: number) => void; diff --git a/src/components/dialogs/network-modifications/line/creation/line-creation-dialog.jsx b/src/components/dialogs/network-modifications/line/creation/line-creation-dialog.jsx index e19cc2d2eb..338ac456b4 100644 --- a/src/components/dialogs/network-modifications/line/creation/line-creation-dialog.jsx +++ b/src/components/dialogs/network-modifications/line/creation/line-creation-dialog.jsx @@ -81,7 +81,7 @@ import { toModificationProperties, } from '../../common/properties/property-utils'; import GridItem from '../../../commons/grid-item'; -import { formatCompleteCurrentLimit } from '../../../../utils/utils.js'; +import { formatCompleteCurrentLimit } from '../../../../utils/utils'; const emptyFormData = { ...getHeaderEmptyFormData(), diff --git a/src/components/dialogs/parameters/voltageinit/voltage-limits-parameters.tsx b/src/components/dialogs/parameters/voltageinit/voltage-limits-parameters.tsx index d974ef0ba2..db0709ddd0 100644 --- a/src/components/dialogs/parameters/voltageinit/voltage-limits-parameters.tsx +++ b/src/components/dialogs/parameters/voltageinit/voltage-limits-parameters.tsx @@ -5,13 +5,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -import DndTable, { - ColumnDirectoryItem, - ColumnNumeric, - ColumnText, - DndColumn, - DndColumnType, -} from 'components/utils/dnd-table/dnd-table'; +import DndTable, { DndColumn, DndColumnType } from 'components/utils/dnd-table/dnd-table'; import { FILTERS, HIGH_VOLTAGE_LIMIT, @@ -48,37 +42,39 @@ const VoltageLimitsParameters = () => { }, [intl]); const VOLTAGE_LIMITS_MODIFICATION_COLUMNS_DEFINITIONS: (DndColumn & { initialValue: any })[] = useMemo(() => { - return [ - { - label: 'VoltageLevelFilter', - dataKey: FILTERS, - initialValue: [], - editable: true, - type: DndColumnType.DIRECTORY_ITEMS as DndColumnType.DIRECTORY_ITEMS, //TODO: ??? - equipmentTypes: [EQUIPMENT_TYPES.VOLTAGE_LEVEL], - elementType: ElementType.FILTER, - titleId: 'FiltersListsSelection', - extra: VoltageLevelFilterTooltip, - }, - { - label: 'LowVoltageLimitAdjustment', - dataKey: LOW_VOLTAGE_LIMIT, - initialValue: null, - editable: true, - type: DndColumnType.NUMERIC as DndColumnType.NUMERIC, - adornment: VoltageAdornment, - textAlign: 'right' as 'right' | 'left', - }, - { - label: 'HighVoltageLimitAdjustment', - dataKey: HIGH_VOLTAGE_LIMIT, - initialValue: null, - editable: true, - type: DndColumnType.NUMERIC as DndColumnType.NUMERIC, - adornment: VoltageAdornment, - textAlign: 'right' as 'right' | 'left', - }, - ].map((column) => ({ + return ( + [ + { + label: 'VoltageLevelFilter', + dataKey: FILTERS, + initialValue: [], + editable: true, + type: DndColumnType.DIRECTORY_ITEMS, + equipmentTypes: [EQUIPMENT_TYPES.VOLTAGE_LEVEL], + elementType: ElementType.FILTER, + titleId: 'FiltersListsSelection', + extra: VoltageLevelFilterTooltip, + }, + { + label: 'LowVoltageLimitAdjustment', + dataKey: LOW_VOLTAGE_LIMIT, + initialValue: null, + editable: true, + type: DndColumnType.NUMERIC, + adornment: VoltageAdornment, + textAlign: 'right', + }, + { + label: 'HighVoltageLimitAdjustment', + dataKey: HIGH_VOLTAGE_LIMIT, + initialValue: null, + editable: true, + type: DndColumnType.NUMERIC, + adornment: VoltageAdornment, + textAlign: 'right', + }, + ] satisfies (DndColumn & { initialValue: any })[] + ).map((column) => ({ ...column, label: intl .formatMessage({ id: column.label }) @@ -94,7 +90,7 @@ const VoltageLimitsParameters = () => { dataKey: FILTERS, initialValue: [], editable: true, - type: DndColumnType.DIRECTORY_ITEMS as DndColumnType.DIRECTORY_ITEMS, //TODO: ??? + type: DndColumnType.DIRECTORY_ITEMS as const, equipmentTypes: [EQUIPMENT_TYPES.VOLTAGE_LEVEL], elementType: ElementType.FILTER, titleId: 'FiltersListsSelection', @@ -105,18 +101,18 @@ const VoltageLimitsParameters = () => { dataKey: LOW_VOLTAGE_LIMIT, initialValue: null, editable: true, - type: DndColumnType.NUMERIC as DndColumnType.NUMERIC, + type: DndColumnType.NUMERIC as const, adornment: VoltageAdornment, - textAlign: 'right' as 'right' | 'left', //TODO ??? + textAlign: 'right' as const, }, { label: 'HighVoltageLimitDefault', dataKey: HIGH_VOLTAGE_LIMIT, initialValue: null, editable: true, - type: DndColumnType.NUMERIC as DndColumnType.NUMERIC, + type: DndColumnType.NUMERIC as const, adornment: VoltageAdornment, - textAlign: 'right' as 'right' | 'left', + textAlign: 'right' as const, }, ].map((column) => ({ ...column, diff --git a/src/components/study-drawer.jsx b/src/components/study-drawer.jsx index 7e28b534ea..e2532a6dd2 100644 --- a/src/components/study-drawer.jsx +++ b/src/components/study-drawer.jsx @@ -7,8 +7,8 @@ import Drawer from '@mui/material/Drawer'; import PropTypes from 'prop-types'; -import { mergeSx } from './utils/functions'; import { DRAWER_NODE_EDITOR_WIDTH } from '../utils/UIconstants'; +import { mergeSx } from '@gridsuite/commons-ui'; const styles = { drawerPaper: { diff --git a/src/components/utils/custom-nested-menu.tsx b/src/components/utils/custom-nested-menu.tsx index 316a1a769f..68100fdeab 100644 --- a/src/components/utils/custom-nested-menu.tsx +++ b/src/components/utils/custom-nested-menu.tsx @@ -4,9 +4,9 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -import { ForwardRefExoticComponent, PropsWithChildren, RefAttributes, useState } from 'react'; +import { PropsWithChildren, useState } from 'react'; import { NestedMenuItem, NestedMenuItemProps } from 'mui-nested-menu'; -import { Box, ExtendButtonBase, MenuItem, MenuItemProps, MenuItemTypeMap, SxProps } from '@mui/material'; +import { Box, MenuItem, MenuItemProps, SxProps } from '@mui/material'; import { mergeSx } from '@gridsuite/commons-ui'; const styles = { diff --git a/src/components/utils/dnd-table/dnd-table-bottom-right-buttons.tsx b/src/components/utils/dnd-table/dnd-table-bottom-right-buttons.tsx index 717304d2f0..771140e131 100644 --- a/src/components/utils/dnd-table/dnd-table-bottom-right-buttons.tsx +++ b/src/components/utils/dnd-table/dnd-table-bottom-right-buttons.tsx @@ -14,7 +14,6 @@ import ArrowDownwardIcon from '@mui/icons-material/ArrowDownward'; import ArrowUpwardIcon from '@mui/icons-material/ArrowUpward'; import { useWatch } from 'react-hook-form'; import { useIntl } from 'react-intl'; -import PropTypes from 'prop-types'; import { SELECTED } from '../field-constants'; export interface DndTableBottomRightButtonsProps { diff --git a/src/components/utils/dnd-table/dnd-table.tsx b/src/components/utils/dnd-table/dnd-table.tsx index 4b36b755d4..7958376695 100644 --- a/src/components/utils/dnd-table/dnd-table.tsx +++ b/src/components/utils/dnd-table/dnd-table.tsx @@ -25,15 +25,9 @@ import { DragDropContext, Draggable, Droppable, DroppableProvided, DropResult } import { useIntl } from 'react-intl'; import DndTableBottomLeftButtons from './dnd-table-bottom-left-buttons'; import DndTableBottomRightButtons from './dnd-table-bottom-right-buttons'; -import { TableNumericalInput, TableNumericalInputProps } from '../rhf-inputs/table-inputs/table-numerical-input'; +import { TableNumericalInput } from '../rhf-inputs/table-inputs/table-numerical-input'; import { TableTextInput } from '../rhf-inputs/table-inputs/table-text-input'; -import { - AutocompleteInput, - CheckboxInput, - ElementType, - RawReadOnlyInput, - RawReadOnlyInputProps, -} from '@gridsuite/commons-ui'; +import { AutocompleteInput, CheckboxInput, ElementType, RawReadOnlyInput } from '@gridsuite/commons-ui'; import PropTypes from 'prop-types'; import { SELECTED } from '../field-constants'; import { ErrorInput } from '@gridsuite/commons-ui'; @@ -100,6 +94,7 @@ interface ColumnBase { label?: string; extra?: JSX.Element; editable?: boolean; + type: DndColumnType; } export interface ColumnText extends ColumnBase { diff --git a/src/components/utils/inputs/input-hooks.tsx b/src/components/utils/inputs/input-hooks.tsx index 58e8773530..b8761c0e00 100644 --- a/src/components/utils/inputs/input-hooks.tsx +++ b/src/components/utils/inputs/input-hooks.tsx @@ -233,7 +233,7 @@ export const useCSVPicker = ({ label, header, resetTrigger, maxTapNumber, disabl ); - }, [_acceptedFile, disabled, header, intl, label, maxTapNumber]); + }, [_acceptedFile, disabled, header, intl, label, maxTapNumber, CSVReader]); return [_acceptedFile, field, fileError] as const; }; diff --git a/src/components/utils/rhf-inputs/enum-input.tsx b/src/components/utils/rhf-inputs/enum-input.tsx index 26ec1a123d..a5a1fdeaae 100644 --- a/src/components/utils/rhf-inputs/enum-input.tsx +++ b/src/components/utils/rhf-inputs/enum-input.tsx @@ -6,20 +6,26 @@ */ import PropTypes from 'prop-types'; -import { FormControl, InputLabel, Select, MenuItem, FormHelperText, FormControlTypeMap } from '@mui/material'; +import { FormControl, InputLabel, Select, MenuItem, FormHelperText } from '@mui/material'; import { FormattedMessage } from 'react-intl'; import { FieldLabel } from '@gridsuite/commons-ui'; import { useController } from 'react-hook-form'; -interface EnumInputProps { +interface EnumInputProps { options: T[]; name: string; label: string; size: 'small' | 'medium'; - labelValues: Record + labelValues: Record; } -const EnumInput = ({ options, name, label, size, labelValues }: EnumInputProps) => { +const EnumInput = ({ + options, + name, + label, + size, + labelValues, +}: EnumInputProps) => { const { field: { onChange, value }, fieldState: { error }, diff --git a/src/components/utils/rhf-inputs/read-only/button-read-only-input.tsx b/src/components/utils/rhf-inputs/read-only/button-read-only-input.tsx index 6f75136e3b..bfde510a4c 100644 --- a/src/components/utils/rhf-inputs/read-only/button-read-only-input.tsx +++ b/src/components/utils/rhf-inputs/read-only/button-read-only-input.tsx @@ -12,7 +12,7 @@ import PropTypes from 'prop-types'; import { useTheme } from '@mui/material'; import { PropsWithChildren } from 'react'; -interface ButtonReadOnlyInputProps extends PropsWithChildren{ +interface ButtonReadOnlyInputProps extends PropsWithChildren { name: string; isNumerical?: boolean; } diff --git a/src/components/utils/rhf-inputs/table-inputs/table-numerical-input.tsx b/src/components/utils/rhf-inputs/table-inputs/table-numerical-input.tsx index 38a20badb7..719907c23b 100644 --- a/src/components/utils/rhf-inputs/table-inputs/table-numerical-input.tsx +++ b/src/components/utils/rhf-inputs/table-inputs/table-numerical-input.tsx @@ -5,14 +5,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -import { - IconButton, - InputAdornment, - InputBaseComponentProps, - StandardTextFieldProps, - TextField, - TextFieldProps, -} from '@mui/material'; +import { IconButton, InputAdornment, InputBaseComponentProps, StandardTextFieldProps, TextField } from '@mui/material'; import { useController, useFormContext } from 'react-hook-form'; import ClearIcon from '@mui/icons-material/Clear'; import { useMemo } from 'react'; diff --git a/src/components/utils/utils.ts b/src/components/utils/utils.ts index 597777269c..98fb54096e 100644 --- a/src/components/utils/utils.ts +++ b/src/components/utils/utils.ts @@ -5,14 +5,14 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -import { useEffect, useRef } from 'react'; import { getIn, SchemaDescription } from 'yup'; import { isBlankOrEmpty, toNumber } from './validation-functions'; import { CURRENT_LIMITS, ID } from './field-constants'; import { addSelectedFieldToRows } from './dnd-table/dnd-table'; -import { CurrentLimits, OperationalLimitsGroup, TemporaryLimit } from 'services/network-modification-types'; +import { OperationalLimitsGroup, TemporaryLimit } from 'services/network-modification-types'; import { VoltageLevel } from './equipment-types'; import { AttributeModification } from 'components/dialogs/network-modifications/hvdc-line/vsc/converter-station/converter-station-utils'; +import { Option } from '@gridsuite/commons-ui'; export const UNDEFINED_ACCEPTABLE_DURATION = Math.pow(2, 31) - 1; @@ -84,8 +84,12 @@ export const areNumbersOrdered = (array?: unknown) => { return true; }; -export const areIdsEqual = (val1: { id: string }, val2: { id: string }) => { - return val1.id === val2.id; +export const areIdsEqual = (val1: Option, val2: Option) => { + if (typeof val1 !== 'string' && typeof val2 !== 'string') { + return val1.id === val2.id; + } else { + return val1 === val2; + } }; export const areUuidsEqual = (val1: { uuid: string }, val2: { uuid: string }) => { diff --git a/src/components/utils/validation-functions.ts b/src/components/utils/validation-functions.ts index 53d246e811..4707010db2 100644 --- a/src/components/utils/validation-functions.ts +++ b/src/components/utils/validation-functions.ts @@ -5,8 +5,6 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -import { ReactiveCapabilityCurvePoint } from "components/dialogs/network-modifications/hvdc-line/vsc/converter-station/converter-station-utils"; - const NO_ERROR = { error: false, errorMsgId: null, @@ -46,7 +44,7 @@ export function isBlankOrEmpty(value: unknown) { /* * Returns true if the value is a valid number, per Gridsuite's standard (allows either coma or dots for decimal). */ -export function validateValueIsANumber(value?: string | number | null | boolean) : value is number{ +export function validateValueIsANumber(value?: string | number | null | boolean): value is number { if (value == null || value === '') { return false; } @@ -59,7 +57,10 @@ export function validateValueIsANumber(value?: string | number | null | boolean) * - the second parameter valueToCompareTo is a valid number * - the first parameter's value is lower or equal to the second's */ -export function validateValueIsLessThanOrEqualTo(value?: string | number | null | boolean, valueToCompareTo?: string | number | null | boolean){ +export function validateValueIsLessThanOrEqualTo( + value?: string | number | null | boolean, + valueToCompareTo?: string | number | null | boolean +) { return ( validateValueIsANumber(value) && validateValueIsANumber(valueToCompareTo) && @@ -73,7 +74,10 @@ export function validateValueIsLessThanOrEqualTo(value?: string | number | null * - the second parameter valueToCompareTo is a valid number * - the first parameter's value is greater or equal to the second's */ -export function validateValueIsGreaterThanOrEqualTo(value?: string | number | null | boolean, valueToCompareTo?: string | number | null | boolean) { +export function validateValueIsGreaterThanOrEqualTo( + value?: string | number | null | boolean, + valueToCompareTo?: string | number | null | boolean +) { return ( validateValueIsANumber(value) && validateValueIsANumber(valueToCompareTo) && @@ -87,7 +91,10 @@ export function validateValueIsGreaterThanOrEqualTo(value?: string | number | nu * - the second parameter valueToCompareTo is a valid number * - the first parameter's value is lower than the second's */ -export function validateValueIsLessThan(value?: string | number | null | boolean, valueToCompareTo?: string | number | null | boolean) { +export function validateValueIsLessThan( + value?: string | number | null | boolean, + valueToCompareTo?: string | number | null | boolean +) { return ( validateValueIsANumber(value) && validateValueIsANumber(valueToCompareTo) && @@ -101,7 +108,10 @@ export function validateValueIsLessThan(value?: string | number | null | boolean * - the second parameter valueToCompareTo is a valid number * - the first parameter's value is greater than the second's */ -export function validateValueIsGreaterThan(value?: string | number | null | boolean, valueToCompareTo?: string | number | null | boolean) { +export function validateValueIsGreaterThan( + value?: string | number | null | boolean, + valueToCompareTo?: string | number | null | boolean +) { return ( validateValueIsANumber(value) && validateValueIsANumber(valueToCompareTo) && @@ -124,7 +134,11 @@ interface ToValidateType { * Rule : if the field is NOT required (toValidate.isFieldRequired is either undefined or equals to false), * then any check that applies to the value will pass if the value is empty. */ -export function validateField(value: string | number | null | undefined | boolean, toValidate: ToValidateType, disabled = false) { +export function validateField( + value: string | number | null | undefined | boolean, + toValidate: ToValidateType, + disabled = false +) { if (disabled && !toValidate.forceValidation) { return NO_ERROR; } diff --git a/src/services/study/index.ts b/src/services/study/index.ts index 57be2f0cf6..aa5d6fbc14 100644 --- a/src/services/study/index.ts +++ b/src/services/study/index.ts @@ -17,6 +17,7 @@ import { COMPUTING_AND_NETWORK_MODIFICATION_TYPE } from '../../utils/report/repo import { EquipmentType } from '@gridsuite/commons-ui'; import { NetworkModificationCopyInfo } from '../../components/graph/menus/network-modification-menu.type'; import { ComputingType } from '../../components/computing-status/computing-type'; +import { Svg } from 'components/diagrams/diagram-common'; export function safeEncodeURIComponent(value: string | null | undefined): string { return value != null ? encodeURIComponent(value) : ''; @@ -167,7 +168,7 @@ export function fetchNodeSeverities( return backendFetchJson(url); } -export function fetchSvg(svgUrl: string) { +export function fetchSvg(svgUrl: string): Promise { console.debug(svgUrl); return backendFetch(svgUrl).then((response) => (response.status === 204 ? null : response.json())); } From 524625193a0844f524c33e55ee5fc515933453bb Mon Sep 17 00:00:00 2001 From: LE SAULNIER Kevin Date: Tue, 25 Feb 2025 10:22:57 +0100 Subject: [PATCH 5/6] fix: last commit Signed-off-by: LE SAULNIER Kevin --- src/components/diagrams/diagram-pane.tsx | 2 +- .../dialogs/limits/limits-side-pane.tsx | 3 +- .../dialogs/limits/temporary-limits-table.tsx | 2 +- .../frequency-reserve-pane.jsx | 3 +- .../substations-generators-ordering-pane.jsx | 3 +- .../phase-tap-changer-pane-steps.jsx | 2 +- .../ratio-tap-changer-pane-steps.jsx | 2 +- .../voltageinit/voltage-limits-parameters.tsx | 67 ++++++++++--------- .../custom-columns/node-alias-table.tsx | 3 +- .../utils/alert-custom-message-node.tsx | 2 +- src/components/utils/custom-nested-menu.tsx | 4 +- .../dnd-table-bottom-right-buttons.tsx | 2 +- src/components/utils/dnd-table/dnd-table.tsx | 52 +------------- .../utils/dnd-table/dnd-table.type.ts | 57 ++++++++++++++++ src/components/utils/validation-functions.ts | 2 +- 15 files changed, 111 insertions(+), 95 deletions(-) create mode 100644 src/components/utils/dnd-table/dnd-table.type.ts diff --git a/src/components/diagrams/diagram-pane.tsx b/src/components/diagrams/diagram-pane.tsx index 316bbfaa98..e1f65cb591 100644 --- a/src/components/diagrams/diagram-pane.tsx +++ b/src/components/diagrams/diagram-pane.tsx @@ -229,7 +229,7 @@ const useDisplayView = (studyUuid: UUID, currentNode: CurrentTreeNode, currentRo console.log('debug', 'createNetworkAreaDiagramView', state); if (ids?.length) { const svgUrl = checkAndGetNetworkAreaDiagramUrl(ids, depth); - return fetchSvgData(svgUrl, DiagramType.NETWORK_AREA_DIAGRAM).then((svg: DiagramSvg) => { + return fetchSvgData(svgUrl, DiagramType.NETWORK_AREA_DIAGRAM).then((svg) => { let nadTitle = ''; let substationsIds: UUID[] = []; svg.additionalMetadata?.voltageLevels diff --git a/src/components/dialogs/limits/limits-side-pane.tsx b/src/components/dialogs/limits/limits-side-pane.tsx index 3a519b5ea7..b236faec2a 100644 --- a/src/components/dialogs/limits/limits-side-pane.tsx +++ b/src/components/dialogs/limits/limits-side-pane.tsx @@ -21,9 +21,10 @@ import { useFieldArray, useFormContext } from 'react-hook-form'; import { formatTemporaryLimits } from '../../utils/utils.js'; import { isNodeBuilt } from '../../graph/util/model-functions'; import { TemporaryLimit } from '../../../services/network-modification-types'; -import DndTable, { ColumnNumeric, ColumnText, DndColumn, DndColumnType } from '../../utils/dnd-table/dnd-table'; +import DndTable from '../../utils/dnd-table/dnd-table'; import TemporaryLimitsTable from './temporary-limits-table'; import { CurrentTreeNode } from '../../../redux/reducer'; +import { ColumnNumeric, ColumnText, DndColumn, DndColumnType } from 'components/utils/dnd-table/dnd-table.type'; export interface LimitsSidePaneProps { limitsGroupFormName: string; diff --git a/src/components/dialogs/limits/temporary-limits-table.tsx b/src/components/dialogs/limits/temporary-limits-table.tsx index e304fa6259..dd42a333fd 100644 --- a/src/components/dialogs/limits/temporary-limits-table.tsx +++ b/src/components/dialogs/limits/temporary-limits-table.tsx @@ -16,7 +16,7 @@ import { SELECTED } from '../../utils/field-constants'; import { TableNumericalInput } from '../../utils/rhf-inputs/table-inputs/table-numerical-input'; import { TableTextInput } from '../../utils/rhf-inputs/table-inputs/table-text-input'; import { TemporaryLimit } from '../../../services/network-modification-types'; -import { ColumnNumeric, ColumnText, DndColumnType } from 'components/utils/dnd-table/dnd-table'; +import { ColumnNumeric, ColumnText, DndColumnType } from 'components/utils/dnd-table/dnd-table.type'; const styles = { columnsStyle: { diff --git a/src/components/dialogs/network-modifications/generation-dispatch/frequency-reserve-pane.jsx b/src/components/dialogs/network-modifications/generation-dispatch/frequency-reserve-pane.jsx index 892888cdf0..23b677cf08 100644 --- a/src/components/dialogs/network-modifications/generation-dispatch/frequency-reserve-pane.jsx +++ b/src/components/dialogs/network-modifications/generation-dispatch/frequency-reserve-pane.jsx @@ -9,11 +9,12 @@ import { FREQUENCY_RESERVE, GENERATORS_FILTERS, GENERATORS_FREQUENCY_RESERVES } import { useIntl } from 'react-intl'; import { useMemo } from 'react'; import { useFieldArray } from 'react-hook-form'; -import DndTable, { DndColumnType } from 'components/utils/dnd-table/dnd-table'; +import DndTable from 'components/utils/dnd-table/dnd-table'; import { EQUIPMENT_TYPES } from 'components/utils/equipment-types'; import { ElementType } from '@gridsuite/commons-ui'; import { Tooltip, IconButton } from '@mui/material'; import InfoIcon from '@mui/icons-material/Info'; +import { DndColumnType } from 'components/utils/dnd-table/dnd-table.type'; const FrequencyReservePane = ({ id = GENERATORS_FREQUENCY_RESERVES }) => { const intl = useIntl(); diff --git a/src/components/dialogs/network-modifications/generation-dispatch/substations-generators-ordering-pane.jsx b/src/components/dialogs/network-modifications/generation-dispatch/substations-generators-ordering-pane.jsx index 935d1824ae..d56cbe04cf 100644 --- a/src/components/dialogs/network-modifications/generation-dispatch/substations-generators-ordering-pane.jsx +++ b/src/components/dialogs/network-modifications/generation-dispatch/substations-generators-ordering-pane.jsx @@ -9,7 +9,8 @@ import { SUBSTATIONS_GENERATORS_ORDERING, SUBSTATION_IDS } from 'components/util import { useIntl } from 'react-intl'; import { useMemo } from 'react'; import { useFieldArray } from 'react-hook-form'; -import DndTable, { DndColumnType } from 'components/utils/dnd-table/dnd-table'; +import DndTable from 'components/utils/dnd-table/dnd-table'; +import { DndColumnType } from 'components/utils/dnd-table/dnd-table.type'; const SubstationsGeneratorsOrderingPane = ({ id = SUBSTATIONS_GENERATORS_ORDERING }) => { const intl = useIntl(); diff --git a/src/components/dialogs/network-modifications/two-windings-transformer/tap-changer-pane/phase-tap-changer-pane/phase-tap-changer-pane-steps.jsx b/src/components/dialogs/network-modifications/two-windings-transformer/tap-changer-pane/phase-tap-changer-pane/phase-tap-changer-pane-steps.jsx index fb4d130a10..aafe13dc49 100644 --- a/src/components/dialogs/network-modifications/two-windings-transformer/tap-changer-pane/phase-tap-changer-pane/phase-tap-changer-pane-steps.jsx +++ b/src/components/dialogs/network-modifications/two-windings-transformer/tap-changer-pane/phase-tap-changer-pane/phase-tap-changer-pane-steps.jsx @@ -20,7 +20,7 @@ import { useIntl } from 'react-intl'; import TapChangerSteps from '../tap-changer-steps'; import { parseIntData } from '../../../../dialog-utils'; import { PHASE_TAP } from '../../creation/two-windings-transformer-creation-dialog'; -import { DndColumnType } from 'components/utils/dnd-table/dnd-table'; +import { DndColumnType } from 'components/utils/dnd-table/dnd-table.type'; const PhaseTapChangerPaneSteps = ({ disabled, previousValues, editData, currentNode, isModification = false }) => { const intl = useIntl(); diff --git a/src/components/dialogs/network-modifications/two-windings-transformer/tap-changer-pane/ratio-tap-changer-pane/ratio-tap-changer-pane-steps.jsx b/src/components/dialogs/network-modifications/two-windings-transformer/tap-changer-pane/ratio-tap-changer-pane/ratio-tap-changer-pane-steps.jsx index 419f27ac3d..06fb68f255 100644 --- a/src/components/dialogs/network-modifications/two-windings-transformer/tap-changer-pane/ratio-tap-changer-pane/ratio-tap-changer-pane-steps.jsx +++ b/src/components/dialogs/network-modifications/two-windings-transformer/tap-changer-pane/ratio-tap-changer-pane/ratio-tap-changer-pane-steps.jsx @@ -19,7 +19,7 @@ import { useIntl } from 'react-intl'; import TapChangerSteps from '../tap-changer-steps'; import { parseIntData } from '../../../../dialog-utils'; import { RATIO_TAP } from '../../creation/two-windings-transformer-creation-dialog'; -import { DndColumnType } from 'components/utils/dnd-table/dnd-table'; +import { DndColumnType } from 'components/utils/dnd-table/dnd-table.type'; const RatioTapChangerPaneSteps = ({ disabled, previousValues, editData, currentNode, isModification = false }) => { const intl = useIntl(); diff --git a/src/components/dialogs/parameters/voltageinit/voltage-limits-parameters.tsx b/src/components/dialogs/parameters/voltageinit/voltage-limits-parameters.tsx index db0709ddd0..1a20506b4d 100644 --- a/src/components/dialogs/parameters/voltageinit/voltage-limits-parameters.tsx +++ b/src/components/dialogs/parameters/voltageinit/voltage-limits-parameters.tsx @@ -5,7 +5,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -import DndTable, { DndColumn, DndColumnType } from 'components/utils/dnd-table/dnd-table'; +import DndTable from 'components/utils/dnd-table/dnd-table'; import { FILTERS, HIGH_VOLTAGE_LIMIT, @@ -24,6 +24,7 @@ import { ElementType } from '@gridsuite/commons-ui'; import { VoltageAdornment } from '../../dialog-utils'; import { styles } from '../parameters'; import Alert from '@mui/material/Alert'; +import { DndColumn, DndColumnType } from 'components/utils/dnd-table/dnd-table.type'; const VoltageLimitsParameters = () => { const intl = useIntl(); @@ -84,37 +85,39 @@ const VoltageLimitsParameters = () => { }, [VoltageLevelFilterTooltip, intl]); const VOLTAGE_LIMITS_DEFAULT_COLUMNS_DEFINITIONS: (DndColumn & { initialValue: any[] | null })[] = useMemo(() => { - return [ - { - label: 'VoltageLevelFilter', - dataKey: FILTERS, - initialValue: [], - editable: true, - type: DndColumnType.DIRECTORY_ITEMS as const, - equipmentTypes: [EQUIPMENT_TYPES.VOLTAGE_LEVEL], - elementType: ElementType.FILTER, - titleId: 'FiltersListsSelection', - extra: VoltageLevelFilterTooltip, - }, - { - label: 'LowVoltageLimitDefault', - dataKey: LOW_VOLTAGE_LIMIT, - initialValue: null, - editable: true, - type: DndColumnType.NUMERIC as const, - adornment: VoltageAdornment, - textAlign: 'right' as const, - }, - { - label: 'HighVoltageLimitDefault', - dataKey: HIGH_VOLTAGE_LIMIT, - initialValue: null, - editable: true, - type: DndColumnType.NUMERIC as const, - adornment: VoltageAdornment, - textAlign: 'right' as const, - }, - ].map((column) => ({ + return ( + [ + { + label: 'VoltageLevelFilter', + dataKey: FILTERS, + initialValue: [], + editable: true, + type: DndColumnType.DIRECTORY_ITEMS, + equipmentTypes: [EQUIPMENT_TYPES.VOLTAGE_LEVEL], + elementType: ElementType.FILTER, + titleId: 'FiltersListsSelection', + extra: VoltageLevelFilterTooltip, + }, + { + label: 'LowVoltageLimitDefault', + dataKey: LOW_VOLTAGE_LIMIT, + initialValue: null, + editable: true, + type: DndColumnType.NUMERIC, + adornment: VoltageAdornment, + textAlign: 'right', + }, + { + label: 'HighVoltageLimitDefault', + dataKey: HIGH_VOLTAGE_LIMIT, + initialValue: null, + editable: true, + type: DndColumnType.NUMERIC, + adornment: VoltageAdornment, + textAlign: 'right', + }, + ] satisfies (DndColumn & { initialValue: any[] | null })[] + ).map((column) => ({ ...column, label: intl .formatMessage({ id: column.label }) diff --git a/src/components/spreadsheet/custom-columns/node-alias-table.tsx b/src/components/spreadsheet/custom-columns/node-alias-table.tsx index cab7159ab6..f0335af1b4 100644 --- a/src/components/spreadsheet/custom-columns/node-alias-table.tsx +++ b/src/components/spreadsheet/custom-columns/node-alias-table.tsx @@ -8,11 +8,12 @@ import React, { useMemo } from 'react'; import { NODE_ALIAS, NODE_NAME, NODES_ALIASES } from './custom-columns-nodes-form-utils'; import { useSelector } from 'react-redux'; -import DndTable, { DndColumn, DndColumnType } from '../../utils/dnd-table/dnd-table'; +import DndTable from '../../utils/dnd-table/dnd-table'; import { SELECTED } from '../../utils/field-constants'; import { useFieldArray } from 'react-hook-form'; import { useIntl } from 'react-intl'; import { AppState, CurrentTreeNode } from '../../../redux/reducer'; +import { DndColumn, DndColumnType } from 'components/utils/dnd-table/dnd-table.type'; const NodeAliasTable = () => { const treeModel = useSelector((state: AppState) => state.networkModificationTreeModel); diff --git a/src/components/utils/alert-custom-message-node.tsx b/src/components/utils/alert-custom-message-node.tsx index b91f7ab534..80eb0412af 100644 --- a/src/components/utils/alert-custom-message-node.tsx +++ b/src/components/utils/alert-custom-message-node.tsx @@ -26,7 +26,7 @@ const AlertCustomMessageNode = (props: AlertCustomMessageNodeProps) => { const { noMargin = false, message } = props; return ( - + ); diff --git a/src/components/utils/custom-nested-menu.tsx b/src/components/utils/custom-nested-menu.tsx index 68100fdeab..a4b3f43a9e 100644 --- a/src/components/utils/custom-nested-menu.tsx +++ b/src/components/utils/custom-nested-menu.tsx @@ -6,7 +6,7 @@ */ import { PropsWithChildren, useState } from 'react'; import { NestedMenuItem, NestedMenuItemProps } from 'mui-nested-menu'; -import { Box, MenuItem, MenuItemProps, SxProps } from '@mui/material'; +import { Box, MenuItem, MenuItemProps, SxProps, Theme } from '@mui/material'; import { mergeSx } from '@gridsuite/commons-ui'; const styles = { @@ -25,7 +25,7 @@ const styles = { }; interface CustomNestedMenuItemProps extends PropsWithChildren, Omit { - sx?: SxProps; + sx?: SxProps; } export const CustomNestedMenuItem = (props: CustomNestedMenuItemProps) => { diff --git a/src/components/utils/dnd-table/dnd-table-bottom-right-buttons.tsx b/src/components/utils/dnd-table/dnd-table-bottom-right-buttons.tsx index 771140e131..01412d2c56 100644 --- a/src/components/utils/dnd-table/dnd-table-bottom-right-buttons.tsx +++ b/src/components/utils/dnd-table/dnd-table-bottom-right-buttons.tsx @@ -39,7 +39,7 @@ const DndTableBottomRightButtons = ({ }: DndTableBottomRightButtonsProps) => { const intl = useIntl(); - const currentRows: any[] = useWatch({ + const currentRows: ({ selected: boolean } & Record)[] = useWatch({ name: arrayFormName, }); diff --git a/src/components/utils/dnd-table/dnd-table.tsx b/src/components/utils/dnd-table/dnd-table.tsx index 7958376695..7aef1d2361 100644 --- a/src/components/utils/dnd-table/dnd-table.tsx +++ b/src/components/utils/dnd-table/dnd-table.tsx @@ -27,7 +27,7 @@ import DndTableBottomLeftButtons from './dnd-table-bottom-left-buttons'; import DndTableBottomRightButtons from './dnd-table-bottom-right-buttons'; import { TableNumericalInput } from '../rhf-inputs/table-inputs/table-numerical-input'; import { TableTextInput } from '../rhf-inputs/table-inputs/table-text-input'; -import { AutocompleteInput, CheckboxInput, ElementType, RawReadOnlyInput } from '@gridsuite/commons-ui'; +import { AutocompleteInput, CheckboxInput, RawReadOnlyInput } from '@gridsuite/commons-ui'; import PropTypes from 'prop-types'; import { SELECTED } from '../field-constants'; import { ErrorInput } from '@gridsuite/commons-ui'; @@ -35,7 +35,7 @@ import { FieldErrorAlert } from '@gridsuite/commons-ui'; import DndTableAddRowsDialog from './dnd-table-add-rows-dialog'; import { DirectoryItemsInput } from '@gridsuite/commons-ui'; import ChipItemsInput from '../rhf-inputs/chip-items-input'; -import { EQUIPMENT_TYPES } from '../equipment-types'; +import { ColumnBase, DndColumn, DndColumnType } from './dnd-table.type'; export const MAX_ROWS_NUMBER = 100; const styles = { @@ -79,54 +79,6 @@ function MultiCheckbox({ arrayFormName, handleClickCheck, handleClickUncheck, .. ); } -export enum DndColumnType { - TEXT = 'TEXT', - NUMERIC = 'NUMERIC', - AUTOCOMPLETE = 'AUTOCOMPLETE', - CHIP_ITEMS = 'CHIP_ITEMS', - DIRECTORY_ITEMS = 'DIRECTORY_ITEMS', -} - -interface ColumnBase { - dataKey: string; - maxWidth?: number | string; - width?: number | string; - label?: string; - extra?: JSX.Element; - editable?: boolean; - type: DndColumnType; -} - -export interface ColumnText extends ColumnBase { - type: DndColumnType.TEXT; - showErrorMsg?: boolean; -} - -export interface ColumnNumeric extends ColumnBase { - type: DndColumnType.NUMERIC; - adornment?: { text: string }; - clearable?: boolean; - textAlign?: 'right' | 'left'; -} - -export interface ColumnAutocomplete extends ColumnBase { - type: DndColumnType.AUTOCOMPLETE; - options: string[]; -} - -export interface ColumnDirectoryItem extends ColumnBase { - type: DndColumnType.DIRECTORY_ITEMS; - equipmentTypes: EQUIPMENT_TYPES[]; - elementType: ElementType; - titleId: string; -} - -export interface ColumnChipsItem extends ColumnBase { - type: DndColumnType.CHIP_ITEMS; -} - -export type DndColumn = ColumnNumeric | ColumnAutocomplete | ColumnText | ColumnDirectoryItem | ColumnChipsItem; - interface DefaultTableCellProps { arrayFormName: string; rowIndex: number; diff --git a/src/components/utils/dnd-table/dnd-table.type.ts b/src/components/utils/dnd-table/dnd-table.type.ts new file mode 100644 index 0000000000..cd32b05d05 --- /dev/null +++ b/src/components/utils/dnd-table/dnd-table.type.ts @@ -0,0 +1,57 @@ +/** + * Copyright (c) 2025, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +import { ElementType } from '@gridsuite/commons-ui'; +import { EQUIPMENT_TYPES } from '../equipment-types'; + +export enum DndColumnType { + TEXT = 'TEXT', + NUMERIC = 'NUMERIC', + AUTOCOMPLETE = 'AUTOCOMPLETE', + CHIP_ITEMS = 'CHIP_ITEMS', + DIRECTORY_ITEMS = 'DIRECTORY_ITEMS', +} + +export interface ColumnBase { + dataKey: string; + maxWidth?: number | string; + width?: number | string; + label?: string; + extra?: JSX.Element; + editable?: boolean; + type: DndColumnType; +} + +export interface ColumnText extends ColumnBase { + type: DndColumnType.TEXT; + showErrorMsg?: boolean; +} + +export interface ColumnNumeric extends ColumnBase { + type: DndColumnType.NUMERIC; + adornment?: { text: string }; + clearable?: boolean; + textAlign?: 'right' | 'left'; +} + +export interface ColumnAutocomplete extends ColumnBase { + type: DndColumnType.AUTOCOMPLETE; + options: string[]; +} + +export interface ColumnDirectoryItem extends ColumnBase { + type: DndColumnType.DIRECTORY_ITEMS; + equipmentTypes: EQUIPMENT_TYPES[]; + elementType: ElementType; + titleId: string; +} + +export interface ColumnChipsItem extends ColumnBase { + type: DndColumnType.CHIP_ITEMS; +} + +export type DndColumn = ColumnNumeric | ColumnAutocomplete | ColumnText | ColumnDirectoryItem | ColumnChipsItem; diff --git a/src/components/utils/validation-functions.ts b/src/components/utils/validation-functions.ts index 4707010db2..5c52e39543 100644 --- a/src/components/utils/validation-functions.ts +++ b/src/components/utils/validation-functions.ts @@ -14,7 +14,7 @@ const NO_ERROR = { * Returns a Number corresponding to provided value, or NaN if not a valid number per * Gridsuite's standard (allows either coma or dots for decimal). */ -export function toNumber(value: any) { +export function toNumber(value: unknown) { if (typeof value === 'number') { return value; } else if (typeof value === 'string') { From 30e18140f3761cade17b1bb33398eefa156bc3ce Mon Sep 17 00:00:00 2001 From: LE SAULNIER Kevin Date: Thu, 27 Feb 2025 10:45:01 +0100 Subject: [PATCH 6/6] fix: PR remarks Signed-off-by: LE SAULNIER Kevin --- .../dialogs/limits/limits-side-pane.tsx | 13 +- .../voltageinit/voltage-limits-parameters.tsx | 166 +++++++++--------- src/components/utils/utils.ts | 14 +- src/components/utils/validation-functions.ts | 6 +- 4 files changed, 96 insertions(+), 103 deletions(-) diff --git a/src/components/dialogs/limits/limits-side-pane.tsx b/src/components/dialogs/limits/limits-side-pane.tsx index b2bdd74e58..d7d339a5b5 100644 --- a/src/components/dialogs/limits/limits-side-pane.tsx +++ b/src/components/dialogs/limits/limits-side-pane.tsx @@ -36,17 +36,6 @@ export interface LimitsSidePaneProps { onlySelectedLimitsGroup: boolean; } -export interface ILimitColumnDef { - label: string; - dataKey: string; - initialValue: string | null; - editable: boolean; - numeric: boolean; - width?: number; - maxWidth?: number; - showErrorMsg?: boolean; -} - export function LimitsSidePane({ limitsGroupFormName, permanentCurrentLimitPreviousValue, @@ -60,7 +49,7 @@ export function LimitsSidePane({ const useFieldArrayOutputTemporaryLimits = useFieldArray({ name: `${limitsGroupFormName}.${TEMPORARY_LIMITS}`, }); - const columnsDefinition: ((ColumnText | ColumnNumeric) & { initialValue: any })[] = useMemo(() => { + const columnsDefinition: ((ColumnText | ColumnNumeric) & { initialValue: string | null })[] = useMemo(() => { return [ { label: 'TemporaryLimitName', diff --git a/src/components/dialogs/parameters/voltageinit/voltage-limits-parameters.tsx b/src/components/dialogs/parameters/voltageinit/voltage-limits-parameters.tsx index 5d2510880e..bb97da9b9c 100644 --- a/src/components/dialogs/parameters/voltageinit/voltage-limits-parameters.tsx +++ b/src/components/dialogs/parameters/voltageinit/voltage-limits-parameters.tsx @@ -42,89 +42,91 @@ const VoltageLimitsParameters = () => { ); }, [intl]); - const VOLTAGE_LIMITS_MODIFICATION_COLUMNS_DEFINITIONS: (DndColumn & { initialValue: any })[] = useMemo(() => { - return ( - [ - { - label: 'VoltageLevelFilter', - dataKey: FILTERS, - initialValue: [], - editable: true, - type: DndColumnType.DIRECTORY_ITEMS, - equipmentTypes: [EQUIPMENT_TYPES.VOLTAGE_LEVEL], - elementType: ElementType.FILTER, - titleId: 'FiltersListsSelection', - extra: VoltageLevelFilterTooltip, - }, - { - label: 'LowVoltageLimitAdjustment', - dataKey: LOW_VOLTAGE_LIMIT, - initialValue: null, - editable: true, - type: DndColumnType.NUMERIC, - adornment: VoltageAdornment, - textAlign: 'right', - }, - { - label: 'HighVoltageLimitAdjustment', - dataKey: HIGH_VOLTAGE_LIMIT, - initialValue: null, - editable: true, - type: DndColumnType.NUMERIC, - adornment: VoltageAdornment, - textAlign: 'right', - }, - ] satisfies (DndColumn & { initialValue: any })[] - ).map((column) => ({ - ...column, - label: intl - .formatMessage({ id: column.label }) - .toLowerCase() - .replace(/^\w/, (c) => c.toUpperCase()), - })); - }, [VoltageLevelFilterTooltip, intl]); + const VOLTAGE_LIMITS_MODIFICATION_COLUMNS_DEFINITIONS: (DndColumn & { initialValue: unknown[] | null })[] = + useMemo(() => { + return ( + [ + { + label: 'VoltageLevelFilter', + dataKey: FILTERS, + initialValue: [], + editable: true, + type: DndColumnType.DIRECTORY_ITEMS, + equipmentTypes: [EQUIPMENT_TYPES.VOLTAGE_LEVEL], + elementType: ElementType.FILTER, + titleId: 'FiltersListsSelection', + extra: VoltageLevelFilterTooltip, + }, + { + label: 'LowVoltageLimitAdjustment', + dataKey: LOW_VOLTAGE_LIMIT, + initialValue: null, + editable: true, + type: DndColumnType.NUMERIC, + adornment: VoltageAdornment, + textAlign: 'right', + }, + { + label: 'HighVoltageLimitAdjustment', + dataKey: HIGH_VOLTAGE_LIMIT, + initialValue: null, + editable: true, + type: DndColumnType.NUMERIC, + adornment: VoltageAdornment, + textAlign: 'right', + }, + ] satisfies (DndColumn & { initialValue: unknown[] | null })[] + ).map((column) => ({ + ...column, + label: intl + .formatMessage({ id: column.label }) + .toLowerCase() + .replace(/^\w/, (c) => c.toUpperCase()), + })); + }, [VoltageLevelFilterTooltip, intl]); - const VOLTAGE_LIMITS_DEFAULT_COLUMNS_DEFINITIONS: (DndColumn & { initialValue: any[] | null })[] = useMemo(() => { - return ( - [ - { - label: 'VoltageLevelFilter', - dataKey: FILTERS, - initialValue: [], - editable: true, - type: DndColumnType.DIRECTORY_ITEMS, - equipmentTypes: [EQUIPMENT_TYPES.VOLTAGE_LEVEL], - elementType: ElementType.FILTER, - titleId: 'FiltersListsSelection', - extra: VoltageLevelFilterTooltip, - }, - { - label: 'LowVoltageLimitDefault', - dataKey: LOW_VOLTAGE_LIMIT, - initialValue: null, - editable: true, - type: DndColumnType.NUMERIC, - adornment: VoltageAdornment, - textAlign: 'right', - }, - { - label: 'HighVoltageLimitDefault', - dataKey: HIGH_VOLTAGE_LIMIT, - initialValue: null, - editable: true, - type: DndColumnType.NUMERIC, - adornment: VoltageAdornment, - textAlign: 'right', - }, - ] satisfies (DndColumn & { initialValue: any[] | null })[] - ).map((column) => ({ - ...column, - label: intl - .formatMessage({ id: column.label }) - .toLowerCase() - .replace(/^\w/, (c) => c.toUpperCase()), - })); - }, [VoltageLevelFilterTooltip, intl]); + const VOLTAGE_LIMITS_DEFAULT_COLUMNS_DEFINITIONS: (DndColumn & { initialValue: unknown[] | null })[] = + useMemo(() => { + return ( + [ + { + label: 'VoltageLevelFilter', + dataKey: FILTERS, + initialValue: [], + editable: true, + type: DndColumnType.DIRECTORY_ITEMS, + equipmentTypes: [EQUIPMENT_TYPES.VOLTAGE_LEVEL], + elementType: ElementType.FILTER, + titleId: 'FiltersListsSelection', + extra: VoltageLevelFilterTooltip, + }, + { + label: 'LowVoltageLimitDefault', + dataKey: LOW_VOLTAGE_LIMIT, + initialValue: null, + editable: true, + type: DndColumnType.NUMERIC, + adornment: VoltageAdornment, + textAlign: 'right', + }, + { + label: 'HighVoltageLimitDefault', + dataKey: HIGH_VOLTAGE_LIMIT, + initialValue: null, + editable: true, + type: DndColumnType.NUMERIC, + adornment: VoltageAdornment, + textAlign: 'right', + }, + ] satisfies (DndColumn & { initialValue: unknown[] | null })[] + ).map((column) => ({ + ...column, + label: intl + .formatMessage({ id: column.label }) + .toLowerCase() + .replace(/^\w/, (c) => c.toUpperCase()), + })); + }, [VoltageLevelFilterTooltip, intl]); const newModificationRowData = useMemo(() => { const newRowData: Record = {}; diff --git a/src/components/utils/utils.ts b/src/components/utils/utils.ts index 03bcfa0e23..9d292dd6d7 100644 --- a/src/components/utils/utils.ts +++ b/src/components/utils/utils.ts @@ -6,8 +6,8 @@ */ import { getIn, SchemaDescription } from 'yup'; -import { isBlankOrEmpty, toNumber } from './validation-functions'; -import { OperationalLimitsGroup, TemporaryLimit } from 'services/network-modification-types'; +import { isNotBlankOrEmpty, toNumber } from './validation-functions'; +import { CurrentLimits, OperationalLimitsGroup, TemporaryLimit } from 'services/network-modification-types'; import { VoltageLevel } from './equipment-types'; import { AttributeModification } from 'components/dialogs/network-modifications/hvdc-line/vsc/converter-station/converter-station-utils'; import { Option } from '@gridsuite/commons-ui'; @@ -133,18 +133,16 @@ export const formatTemporaryLimits = (temporaryLimits: TemporaryLimit[]) => }; }); -export const formatCompleteCurrentLimit = (completeLimitsGroups: OperationalLimitsGroup[]) => { +export const formatCompleteCurrentLimit = (completeLimitsGroups: CurrentLimits[]) => { const formattedCompleteLimitsGroups: OperationalLimitsGroup[] = []; if (completeLimitsGroups) { completeLimitsGroups.forEach((elt) => { - if (!isBlankOrEmpty(elt.id)) { + if (isNotBlankOrEmpty(elt.id)) { formattedCompleteLimitsGroups.push({ [ID]: elt.id, [CURRENT_LIMITS]: { - permanentLimit: elt.currentLimits.permanentLimit, //TODO: check change is ok ? - temporaryLimits: addSelectedFieldToRows( - formatTemporaryLimits(elt.currentLimits.temporaryLimits) - ), + permanentLimit: elt.permanentLimit, + temporaryLimits: addSelectedFieldToRows(formatTemporaryLimits(elt.temporaryLimits)), }, }); } diff --git a/src/components/utils/validation-functions.ts b/src/components/utils/validation-functions.ts index 5c52e39543..07cfc0c076 100644 --- a/src/components/utils/validation-functions.ts +++ b/src/components/utils/validation-functions.ts @@ -27,11 +27,15 @@ export function toNumber(value: unknown) { return NaN; } +export function isNotBlankOrEmpty(value: T): value is NonNullable { + return !isBlankOrEmpty(value); +} + /* * Returns true if value is either undefined, null, empty or only contains whitespaces. * Otherwise, if value is a boolean or a number, returns false. */ -export function isBlankOrEmpty(value: unknown) { +export function isBlankOrEmpty(value: T) { if (value === undefined || value === null) { return true; }