diff --git a/src/components/diagrams/diagram-common.ts b/src/components/diagrams/diagram-common.ts index 4f89ebd820..d7108b1bbc 100644 --- a/src/components/diagrams/diagram-common.ts +++ b/src/components/diagrams/diagram-common.ts @@ -13,6 +13,8 @@ import { FEEDER_TYPES, FeederTypes } from 'components/utils/feederType'; import { EQUIPMENT_TYPES } from 'components/utils/equipment-types'; import { Theme } from '@mui/material'; import { AppDispatch } from '../../redux/store'; +import { SLDMetadata } from '@powsybl/diagram-viewer'; +import { UUID } from 'crypto'; export const LOADING_WIDTH = 300; export const LOADING_HEIGHT = 300; @@ -138,7 +140,7 @@ export enum DiagramType { } // be careful when using this method because there are treatments made on purpose -export function getEquipmentTypeFromFeederType(feederType: FeederTypes) { +export function getEquipmentTypeFromFeederType(feederType: FeederTypes | null): EQUIPMENT_TYPES | null { switch (feederType) { case FEEDER_TYPES.LINE: return EQUIPMENT_TYPES.LINE; @@ -260,9 +262,25 @@ export const useDiagram = () => { }; }; -export const NoSvg = { +export interface Svg { + svg: string | null; + metadata: SLDMetadata | null; + additionalMetadata: + | (SLDMetadata & { + country: string; + substationId?: string; + voltageLevels: { name: string; substationId: UUID }[]; + nbVoltageLevels?: number; + }) + | null; + error?: string | null; + svgUrl?: string | null; +} + +export const NoSvg: Svg = { svg: null, metadata: null, additionalMetadata: null, - error: null, + error: undefined, + svgUrl: undefined, }; diff --git a/src/components/diagrams/diagram-footer.jsx b/src/components/diagrams/diagram-footer.tsx similarity index 60% rename from src/components/diagrams/diagram-footer.jsx rename to src/components/diagrams/diagram-footer.tsx index 5ffcec9d1a..23b5fa2ebf 100644 --- a/src/components/diagrams/diagram-footer.jsx +++ b/src/components/diagrams/diagram-footer.tsx @@ -12,29 +12,29 @@ import RemoveCircleIcon from '@mui/icons-material/RemoveCircle'; import IconButton from '@mui/material/IconButton'; import FullscreenExitIcon from '@mui/icons-material/FullscreenExit'; import FullscreenIcon from '@mui/icons-material/Fullscreen'; -import PropTypes from 'prop-types'; +import { Theme } from '@mui/material'; const styles = { - counterText: (theme) => ({ + counterText: (theme: Theme) => ({ bottom: theme.spacing(4), left: theme.spacing(1), position: 'absolute', }), - incrementCounterIcon: (theme) => ({ + incrementCounterIcon: (theme: Theme) => ({ padding: 0, bottom: theme.spacing(1), left: theme.spacing(5.5), position: 'absolute', cursor: 'pointer', }), - decrementCounterIcon: (theme) => ({ + decrementCounterIcon: (theme: Theme) => ({ padding: 0, bottom: theme.spacing(1), left: theme.spacing(2), position: 'absolute', cursor: 'pointer', }), - fullScreenIcon: (theme) => ({ + fullScreenIcon: (theme: Theme) => ({ bottom: theme.spacing(1), right: theme.spacing(2), position: 'absolute', @@ -42,8 +42,46 @@ const styles = { }), }; -const DiagramFooter = (props) => { - const { onStopFullScreen, onStartFullScreen, onIncrementCounter, onDecrementCounter } = props; +interface DiagramFooterProps { + showCounterControls?: boolean; + showCounterValue?: boolean; + showFullscreenControl?: boolean; + counterText: string; + counterValue: number; + fullScreenActive?: boolean; + decrementCounterDisabled?: boolean; + incrementCounterDisabled?: boolean; + onIncrementCounter?: () => void; + onDecrementCounter?: () => void; + onStopFullScreen?: () => void; + onStartFullScreen?: () => void; +} + +const defaultProps: DiagramFooterProps = { + showCounterControls: false, + showCounterValue: true, + showFullscreenControl: false, + counterText: '', + counterValue: 0, + fullScreenActive: false, + decrementCounterDisabled: true, + incrementCounterDisabled: false, +}; + +const DiagramFooter: React.FC = ({ + showCounterControls = defaultProps.showCounterControls, + showCounterValue = defaultProps.showCounterValue, + showFullscreenControl = defaultProps.showFullscreenControl, + counterText = defaultProps.counterText, + counterValue = defaultProps.counterValue, + fullScreenActive = defaultProps.fullScreenActive, + decrementCounterDisabled = defaultProps.decrementCounterDisabled, + incrementCounterDisabled = defaultProps.incrementCounterDisabled, + onIncrementCounter, + onDecrementCounter, + onStopFullScreen, + onStartFullScreen, +}) => { const handleStopFullScreen = useCallback(() => onStopFullScreen && onStopFullScreen(), [onStopFullScreen]); const handleStartFullScreen = useCallback(() => onStartFullScreen && onStartFullScreen(), [onStartFullScreen]); const handleIncrementCounter = useCallback(() => onIncrementCounter && onIncrementCounter(), [onIncrementCounter]); @@ -51,65 +89,35 @@ const DiagramFooter = (props) => { return (
- {props.showCounterControls && ( + {showCounterControls && ( <> - {props.showCounterValue && ( - {props.counterText + props.counterValue} - )} + {showCounterValue && {counterText + counterValue}} )} - {props.showFullscreenControl && ( + {showFullscreenControl && ( <> - {props.fullScreenActive && ( + {fullScreenActive && ( )} - {!props.fullScreenActive && ( - - )} + {!fullScreenActive && } )}
); }; -DiagramFooter.defaultProps = { - showCounterControls: false, - showCounterValue: true, - showFullscreenControl: false, - counterText: '', - counterValue: 0, - fullscreenActive: false, - decrementCounterDisabled: true, - incrementCounterDisabled: false, -}; - -DiagramFooter.propTypes = { - showCounterControls: PropTypes.bool, - showCounterValue: PropTypes.bool, - counterText: PropTypes.string, - counterValue: PropTypes.number, - onIncrementCounter: PropTypes.func, - onDecrementCounter: PropTypes.func, - showFullscreenControl: PropTypes.bool, - fullScreenActive: PropTypes.any, - onStopFullScreen: PropTypes.func, - onStartFullScreen: PropTypes.func, - decrementCounterDisabled: PropTypes.bool, - incrementCounterDisabled: PropTypes.bool, -}; - export default DiagramFooter; diff --git a/src/components/diagrams/diagram-header.jsx b/src/components/diagrams/diagram-header.tsx similarity index 74% rename from src/components/diagrams/diagram-header.jsx rename to src/components/diagrams/diagram-header.tsx index 244769ad8f..339c121104 100644 --- a/src/components/diagrams/diagram-header.jsx +++ b/src/components/diagrams/diagram-header.tsx @@ -14,14 +14,15 @@ 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 PropTypes from 'prop-types'; import { stopDiagramBlink } from '../../redux/actions'; import { mergeSx } from '../utils/functions'; +import { Theme } from '@mui/material'; +import { AppState } from 'redux/reducer'; const BLINK_LENGTH_MS = 1800; const styles = { - header: (theme) => ({ + header: (theme: Theme) => ({ // prevent header from making the window wider, prevent bugs when displaying a lot of different voltage levels position: 'absolute', width: '100%', @@ -34,20 +35,20 @@ const styles = { borderBottom: 'solid 1px', borderBottomColor: theme.palette.mode === 'light' ? theme.palette.action.selected : 'transparent', }), - actionIcon: (theme) => ({ + actionIcon: (theme: Theme) => ({ padding: 0, borderRight: theme.spacing(1), }), - pinRotate: (theme) => ({ + pinRotate: (theme: Theme) => ({ padding: 0, borderRight: theme.spacing(1), transform: 'rotate(45deg)', }), - close: (theme) => ({ + close: (theme: Theme) => ({ padding: 0, borderRight: theme.spacing(1), }), - blink: (theme) => ({ + blink: (theme: Theme) => ({ animation: 'diagramHeaderBlinkAnimation ' + BLINK_LENGTH_MS + 'ms', '@keyframes diagramHeaderBlinkAnimation': { // This adds a global css rule, so we keep the rule's name specific. @@ -62,10 +63,33 @@ const styles = { }), }; -const DiagramHeader = (props) => { +interface DiagramHeaderProps { + diagramTitle?: string; + showMinimizeControl?: boolean; + onMinimize?: () => void; + showTogglePinControl?: boolean; + onTogglePin?: () => void; + pinned?: boolean; + showCloseControl?: boolean; + onClose?: () => void; + diagramId?: string; + svgType?: string; +} + +const DiagramHeader: React.FC = ({ + diagramTitle, + showMinimizeControl = false, + onMinimize, + showTogglePinControl = false, + onTogglePin, + pinned, + showCloseControl = false, + onClose, + diagramId, + svgType, +}) => { const dispatch = useDispatch(); - const { onMinimize, onTogglePin, onClose } = props; const handleMinimize = useCallback(() => onMinimize && onMinimize(), [onMinimize]); const handleTogglePin = useCallback(() => onTogglePin && onTogglePin(), [onTogglePin]); const handleClose = useCallback(() => onClose && onClose(), [onClose]); @@ -76,9 +100,8 @@ const DiagramHeader = (props) => { const [blinking, setBlinking] = useState(false); const needsToBlink = useSelector( - (state) => - state.diagramStates.find((diagram) => diagram.svgType === props?.svgType && diagram.id === props?.diagramId) - ?.needsToBlink + (state: AppState) => + state.diagramStates.find((diagram) => diagram.svgType === svgType && diagram.id === diagramId)?.needsToBlink ); useEffect(() => { @@ -99,7 +122,7 @@ const DiagramHeader = (props) => { return ( - + { flexDirection: 'row', }} > - {props.showMinimizeControl && ( + {showMinimizeControl && ( )} - {props.showTogglePinControl && ( - - {props.pinned ? : } + {showTogglePinControl && ( + + {pinned ? : } )} - {props.showCloseControl && ( + {showCloseControl && ( @@ -128,23 +151,4 @@ const DiagramHeader = (props) => { ); }; -DiagramHeader.defaultProps = { - showMinimizeControl: false, - showTogglePinControl: false, - showCloseControl: false, -}; - -DiagramHeader.propTypes = { - diagramTitle: PropTypes.string, - showMinimizeControl: PropTypes.bool, - onMinimize: PropTypes.func, - showTogglePinControl: PropTypes.bool, - onTogglePin: PropTypes.func, - pinned: PropTypes.bool, - showCloseControl: PropTypes.bool, - onClose: PropTypes.func, - diagramId: PropTypes.string, - svgType: PropTypes.string.isRequired, -}; - export default DiagramHeader; diff --git a/src/components/diagrams/diagram-pane.jsx b/src/components/diagrams/diagram-pane.tsx similarity index 86% rename from src/components/diagrams/diagram-pane.jsx rename to src/components/diagrams/diagram-pane.tsx index cb669d4b05..14b8a91c5a 100644 --- a/src/components/diagrams/diagram-pane.jsx +++ b/src/components/diagrams/diagram-pane.tsx @@ -16,8 +16,7 @@ import { PARAM_SUBSTATION_LAYOUT, PARAM_USE_NAME, } from '../../utils/config-params'; -import PropTypes from 'prop-types'; -import { Chip, Stack } from '@mui/material'; +import { Chip, Stack, Theme } from '@mui/material'; import ArrowUpwardIcon from '@mui/icons-material/ArrowUpward'; import TimelineIcon from '@mui/icons-material/Timeline'; import { @@ -32,6 +31,7 @@ import { MAP_BOTTOM_OFFSET, NETWORK_AREA_DIAGRAM_NB_MAX_VOLTAGE_LEVELS, NoSvg, + Svg, useDiagram, ViewState, } from './diagram-common'; @@ -52,20 +52,24 @@ import { fetchSvg, getNetworkAreaDiagramUrl } from '../../services/study'; import { mergeSx } from '../utils/functions'; import { Box } from '@mui/system'; import { useLocalizedCountries } from 'components/utils/localized-countries-hook'; +import { UUID } from 'crypto'; +import { AppState, DiagramState, TreeNodeData } from 'redux/reducer'; +import { Node } from 'reactflow'; +import { SLDMetadata } from '@powsybl/diagram-viewer'; // Returns a callback that returns a promise -const useDisplayView = (studyUuid, currentNode) => { +const useDisplayView = (studyUuid: UUID, currentNode: Node) => { const { snackError } = useSnackMessage(); - const paramUseName = useSelector((state) => state[PARAM_USE_NAME]); + const paramUseName = useSelector((state: AppState) => state[PARAM_USE_NAME]); const { getNameOrId } = useNameOrId(); - const centerName = useSelector((state) => state[PARAM_CENTER_LABEL]); - const diagonalName = useSelector((state) => state[PARAM_DIAGONAL_LABEL]); - const substationLayout = useSelector((state) => state[PARAM_SUBSTATION_LAYOUT]); - const componentLibrary = useSelector((state) => state[PARAM_COMPONENT_LIBRARY]); - const language = useSelector((state) => state[PARAM_LANGUAGE]); + const centerName = useSelector((state: AppState) => state[PARAM_CENTER_LABEL]); + const diagonalName = useSelector((state: AppState) => state[PARAM_DIAGONAL_LABEL]); + const substationLayout = useSelector((state: AppState) => state[PARAM_SUBSTATION_LAYOUT]); + const componentLibrary = useSelector((state: AppState) => state[PARAM_COMPONENT_LIBRARY]); + const language = useSelector((state: AppState) => state[PARAM_LANGUAGE]); const checkAndGetVoltageLevelSingleLineDiagramUrl = useCallback( - (voltageLevelId) => + (voltageLevelId: UUID) => isNodeBuilt(currentNode) ? getVoltageLevelSingleLineDiagram( studyUuid, @@ -83,7 +87,7 @@ const useDisplayView = (studyUuid, currentNode) => { ); const checkAndGetSubstationSingleLineDiagramUrl = useCallback( - (voltageLevelId) => + (voltageLevelId: UUID) => isNodeBuilt(currentNode) ? getSubstationSingleLineDiagram( studyUuid, @@ -99,9 +103,9 @@ const useDisplayView = (studyUuid, currentNode) => { : null, [centerName, componentLibrary, diagonalName, studyUuid, substationLayout, paramUseName, currentNode, language] ); - const initNadWithGeoData = useSelector((state) => state[PARAM_INIT_NAD_WITH_GEO_DATA]); + const initNadWithGeoData = useSelector((state: AppState) => state[PARAM_INIT_NAD_WITH_GEO_DATA]); const checkAndGetNetworkAreaDiagramUrl = useCallback( - (voltageLevelsIds, depth) => + (voltageLevelsIds: UUID[], depth: number) => isNodeBuilt(currentNode) ? getNetworkAreaDiagramUrl(studyUuid, currentNode?.id, voltageLevelsIds, depth, initNadWithGeoData) : null, @@ -110,16 +114,17 @@ const useDisplayView = (studyUuid, currentNode) => { // this callback returns a promise const fetchSvgData = useCallback( - (svgUrl, svgType) => { + (svgUrl: string | null, svgType: DiagramType): Promise => { if (svgUrl) { - return fetchSvg(svgUrl) - .then((data) => { + const fetchSvgPromise: Promise = fetchSvg(svgUrl); + return fetchSvgPromise + .then((data: Svg | null) => { if (data !== null) { return { svg: data.svg, metadata: data.metadata, additionalMetadata: data.additionalMetadata, - error: null, + error: undefined, }; } else { return NoSvg; @@ -157,12 +162,12 @@ const useDisplayView = (studyUuid, currentNode) => { // this callback returns a promise return useCallback( - (diagramState) => { + (diagramState: Partial) => { if (!studyUuid || !currentNode) { return Promise.reject(); } - function createSubstationDiagramView(id, state) { + function createSubstationDiagramView(id: UUID, state: ViewState | undefined) { const svgUrl = checkAndGetSubstationSingleLineDiagramUrl(id); return fetchSvgData(svgUrl, DiagramType.SUBSTATION).then((svg) => { let label = getNameOrId(svg.additionalMetadata) ?? id; @@ -179,7 +184,7 @@ const useDisplayView = (studyUuid, currentNode) => { }); } - function createVoltageLevelDiagramView(id, state) { + 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; @@ -198,19 +203,25 @@ const useDisplayView = (studyUuid, currentNode) => { }); } - function createNetworkAreaDiagramView(ids, state, depth = 0) { + function createNetworkAreaDiagramView(ids: UUID[] | undefined, state: ViewState | undefined, depth = 0) { + console.log('debug', 'createNetworkAreaDiagramView', state); if (ids?.length) { - const svgUrl = checkAndGetNetworkAreaDiagramUrl(ids, depth, initNadWithGeoData); + const svgUrl = checkAndGetNetworkAreaDiagramUrl(ids, depth); return fetchSvgData(svgUrl, DiagramType.NETWORK_AREA_DIAGRAM).then((svg) => { let nadTitle = ''; - let substationsIds = []; + let substationsIds: UUID[] = []; svg.additionalMetadata?.voltageLevels - .map((vl) => ({ + .map((vl: { name: string; substationId: UUID }) => ({ name: getNameOrId(vl), substationId: vl.substationId, })) - .sort((vlA, vlB) => vlA.name.toLowerCase().localeCompare(vlB.name.toLowerCase())) - .forEach((voltageLevel) => { + .sort( + ( + vlA: { name: string; substationId: UUID }, + vlB: { name: string; substationId: UUID } + ) => vlA.name.toLowerCase().localeCompare(vlB.name.toLowerCase()) + ) + .forEach((voltageLevel: { name: string; substationId: UUID }) => { const name = voltageLevel.name; if (name !== null) { nadTitle += (nadTitle !== '' ? ', ' : '') + name; @@ -237,9 +248,9 @@ const useDisplayView = (studyUuid, currentNode) => { } if (diagramState.svgType === DiagramType.VOLTAGE_LEVEL) { - return createVoltageLevelDiagramView(diagramState.id, diagramState.state); + return createVoltageLevelDiagramView(diagramState.id!, diagramState.state); } else if (diagramState.svgType === DiagramType.SUBSTATION) { - return createSubstationDiagramView(diagramState.id, diagramState.state); + return createSubstationDiagramView(diagramState.id!, diagramState.state); } else if (diagramState.svgType === DiagramType.NETWORK_AREA_DIAGRAM) { return createNetworkAreaDiagramView(diagramState.ids, diagramState.state, diagramState.depth); } @@ -252,7 +263,6 @@ const useDisplayView = (studyUuid, currentNode) => { studyUuid, currentNode, fetchSvgData, - initNadWithGeoData, ] ); }; @@ -272,7 +282,7 @@ const styles = { display: 'flex', overflow: 'hidden', }, - availableDiagramSurfaceArea: (theme) => ({ + availableDiagramSurfaceArea: (theme: Theme) => ({ flexDirection: 'row', display: 'inline-flex', paddingRight: theme.spacing(6), @@ -282,22 +292,47 @@ const styles = { }, }; -export function DiagramPane({ studyUuid, showInSpreadsheet, currentNode, visible, oneBusShortCircuitStatus }) { +interface DiagramPaneProps { + studyUuid: UUID; + currentNode: Node; + showInSpreadsheet: (equipment: { equipmentId: string | null; type: string | null }) => void; + visible: boolean; +} + +type DiagramView = { + id: UUID; + ids?: UUID[]; + svgType: DiagramType; + state: ViewState; + name: string; + align: 'left' | 'right' | 'center'; + loadingState: boolean; + metadata?: SLDMetadata; + svg?: string; + country?: string; + depth?: number; + error?: string; + nodeId?: UUID; + additionalMetadata?: any; + fetchSvg?: () => Promise>; +}; + +export function DiagramPane({ studyUuid, currentNode, showInSpreadsheet, visible }: DiagramPaneProps) { const dispatch = useDispatch(); const intl = useIntl(); - const studyUpdatedForce = useSelector((state) => state.studyUpdated); - const [views, setViews] = useState([]); - const fullScreenDiagram = useSelector((state) => state.fullScreenDiagram); + const studyUpdatedForce = useSelector((state: AppState) => state.studyUpdated); + const [views, setViews] = useState([]); + const fullScreenDiagram = useSelector((state: AppState) => state.fullScreenDiagram); const createView = useDisplayView(studyUuid, currentNode); - const diagramStates = useSelector((state) => state.diagramStates); - const networkAreaDiagramDepth = useSelector((state) => state.networkAreaDiagramDepth); + const diagramStates = useSelector((state: AppState) => state.diagramStates); + const networkAreaDiagramDepth = useSelector((state: AppState) => state.networkAreaDiagramDepth); const previousNetworkAreaDiagramDepth = useRef(networkAreaDiagramDepth); - const networkAreaDiagramNbVoltageLevels = useSelector((state) => state.networkAreaDiagramNbVoltageLevels); + const networkAreaDiagramNbVoltageLevels = useSelector((state: AppState) => state.networkAreaDiagramNbVoltageLevels); const { translate } = useLocalizedCountries(); - const notificationIdList = useSelector((state) => state.notificationIdList); + const notificationIdList = useSelector((state: AppState) => state.notificationIdList); const [diagramContentSizes, setDiagramContentSizes] = useState(new Map()); // When a diagram content gets its size from the backend, it will update this map of sizes. useEffect(() => { @@ -305,11 +340,10 @@ export function DiagramPane({ studyUuid, showInSpreadsheet, currentNode, visible }, [diagramStates, studyUuid]); const { openDiagramView, closeDiagramView, closeDiagramViews } = useDiagram(); - const currentNodeRef = useRef(); - currentNodeRef.current = currentNode; - const viewsRef = useRef([]); - viewsRef.current = views; + const currentNodeRef = useRef(currentNode); + const viewsRef = useRef([]); + viewsRef.current = views; /** * BUILDS THE DIAGRAMS LIST * @@ -321,13 +355,20 @@ export function DiagramPane({ studyUuid, showInSpreadsheet, currentNode, visible // Check if we need to add new SLDs in the 'views' and add them if necessary const addMissingSLDs = useCallback( - (diagramStates) => { + (diagramStates: DiagramState[]) => { // We check if we need to add new diagrams - const diagramsToAdd = []; + const diagramsToAdd: { + id: UUID; + svgType: DiagramType; + state: ViewState; + loadingState: boolean; + align: 'left' | 'right' | 'center'; + name: string; + }[] = []; diagramStates.forEach((diagramState) => { if (diagramState.svgType !== DiagramType.NETWORK_AREA_DIAGRAM) { const diagramAlreadyPresentInViews = viewsRef.current.find( - (diagramView) => + (diagramView: DiagramState) => diagramView.svgType !== DiagramType.NETWORK_AREA_DIAGRAM && diagramView.id === diagramState.id ); @@ -352,7 +393,7 @@ export function DiagramPane({ studyUuid, showInSpreadsheet, currentNode, visible // Then we add the data when the fetch is finished diagramsToAdd.forEach((diagramState) => { - createView(diagramState).then((singleLineDiagramView) => { + createView(diagramState)?.then((singleLineDiagramView) => { setViews((views) => { const diagramViewId = views.findIndex( (view) => @@ -364,7 +405,7 @@ export function DiagramPane({ studyUuid, showInSpreadsheet, currentNode, visible ...updatedViews[diagramViewId], ...singleLineDiagramView, loadingState: false, - }; + } as unknown as DiagramView; return updatedViews; }); }); @@ -375,13 +416,13 @@ export function DiagramPane({ studyUuid, showInSpreadsheet, currentNode, visible ); // Check if we need to remove old SLDs from the 'views' and remove them if necessary - const removeObsoleteSLDs = useCallback((diagramStates) => { + const removeObsoleteSLDs = useCallback((diagramStates: DiagramState[]) => { // We check if we need to remove old diagrams - const diagramIdsToRemove = []; + const diagramIdsToRemove: UUID[] = []; viewsRef.current.forEach((diagramView) => { if (diagramView.svgType !== DiagramType.NETWORK_AREA_DIAGRAM) { const diagramStillPresentInRedux = diagramStates.find( - (diagramState) => + (diagramState: DiagramState) => diagramState.svgType !== DiagramType.NETWORK_AREA_DIAGRAM && diagramState.id === diagramView.id ); if (!diagramStillPresentInRedux) { @@ -403,7 +444,7 @@ export function DiagramPane({ studyUuid, showInSpreadsheet, currentNode, visible // Check if we need to remove or add SLDs const updateSLDs = useCallback( - (diagramStates) => { + (diagramStates: DiagramState[]) => { removeObsoleteSLDs(diagramStates); addMissingSLDs(diagramStates); }, @@ -412,10 +453,10 @@ export function DiagramPane({ studyUuid, showInSpreadsheet, currentNode, visible // Add a new NAD in the 'views' (if a NAD is already present, we replace it) const addOrReplaceNAD = useCallback( - (networkAreaIds, networkAreaViewState, networkAreaDiagramDepth) => { + (networkAreaIds: UUID[], networkAreaViewState: ViewState, networkAreaDiagramDepth: number) => { // First we add the empty diagram in the views setViews((views) => { - const newDiagram = { + const newDiagram: DiagramView = { id: networkAreaIds[0], ids: networkAreaIds, name: intl.formatMessage({ id: 'LoadingOf' }, { value: networkAreaIds.toString() }), @@ -446,7 +487,7 @@ export function DiagramPane({ studyUuid, showInSpreadsheet, currentNode, visible state: networkAreaViewState, svgType: DiagramType.NETWORK_AREA_DIAGRAM, depth: networkAreaDiagramDepth, - }).then((networkAreaDiagramView) => { + })?.then((networkAreaDiagramView) => { setViews((views) => { const updatedViews = views.slice(); const nadViewId = views.findIndex((view) => view.svgType === DiagramType.NETWORK_AREA_DIAGRAM); @@ -454,9 +495,11 @@ export function DiagramPane({ studyUuid, showInSpreadsheet, currentNode, visible ...updatedViews[nadViewId], ...networkAreaDiagramView, loadingState: false, - }; + } as unknown as DiagramView; dispatch( - setNetworkAreaDiagramNbVoltageLevels(networkAreaDiagramView.additionalMetadata?.nbVoltageLevels) + setNetworkAreaDiagramNbVoltageLevels( + networkAreaDiagramView.additionalMetadata?.nbVoltageLevels ?? 0 + ) ); return updatedViews; }); @@ -473,9 +516,9 @@ export function DiagramPane({ studyUuid, showInSpreadsheet, currentNode, visible }, []); const updateNAD = useCallback( - (diagramStates) => { + (diagramStates: DiagramState[]) => { previousNetworkAreaDiagramDepth.current = networkAreaDiagramDepth; - const networkAreaIds = []; + const networkAreaIds: UUID[] = []; let networkAreaViewState = ViewState.OPENED; diagramStates.forEach((diagramState) => { if (diagramState.svgType === DiagramType.NETWORK_AREA_DIAGRAM) { @@ -504,14 +547,14 @@ export function DiagramPane({ studyUuid, showInSpreadsheet, currentNode, visible ); // Update the state of the diagrams (opened, minimized, etc) in the 'views' - const updateDiagramStates = useCallback((diagramStates) => { + const updateDiagramStates = useCallback((diagramStates: DiagramState[]) => { // We check if we need to update some diagrams - let diagramsToUpdate = []; - diagramStates.forEach((diagramState) => { + let diagramsToUpdate: { index: number; state: ViewState }[] = []; + diagramStates.forEach((diagramState: DiagramState) => { // if SLD if (diagramState.svgType !== DiagramType.NETWORK_AREA_DIAGRAM) { const diagramIndex = viewsRef.current.findIndex( - (diagramView) => + (diagramView: DiagramView) => diagramView.svgType !== DiagramType.NETWORK_AREA_DIAGRAM && diagramView.id === diagramState.id && diagramView.state !== diagramState.state @@ -529,7 +572,7 @@ export function DiagramPane({ studyUuid, showInSpreadsheet, currentNode, visible // no need to check the ID because we have only one NAD in the views // diagramIndex can only be -1 (if no match) or viewsRef.current.length - 1 (if match) const diagramIndex = viewsRef.current.findIndex( - (diagramView) => + (diagramView: DiagramView) => diagramView.svgType === DiagramType.NETWORK_AREA_DIAGRAM && diagramView.state !== diagramState.state ); @@ -568,7 +611,7 @@ export function DiagramPane({ studyUuid, showInSpreadsheet, currentNode, visible // and then stop before we get too close to // NETWORK_AREA_DIAGRAM_NB_MAX_VOLTAGE_LEVELS const shouldDebounceUpdateNAD = useCallback( - (networkAreaDiagramDepth) => { + (networkAreaDiagramDepth: number) => { const estimatedNbVoltageLevels = getEstimatedNbVoltageLevels( previousNetworkAreaDiagramDepth.current, networkAreaDiagramDepth, @@ -619,20 +662,19 @@ export function DiagramPane({ studyUuid, showInSpreadsheet, currentNode, visible .filter((view) => [ViewState.OPENED, ViewState.PINNED].includes(view.state)) .sort(makeDiagramSorter(diagramStates)); const minimizedDiagrams = views.filter((view) => [ViewState.MINIMIZED].includes(view.state)); - /** * MINIMIZED DIAGRAMS' CONTROLS */ const handleCloseDiagramView = useCallback( - (id, type) => { + (id: UUID, type: DiagramType) => { closeDiagramView(id, type); }, [closeDiagramView] ); const handleOpenDiagramView = useCallback( - (id, type) => { + (id: UUID, type: DiagramType) => { if (!studyUuid || !currentNode) { return; } @@ -647,7 +689,7 @@ export function DiagramPane({ studyUuid, showInSpreadsheet, currentNode, visible // Updates particular diagrams from their IDs const updateDiagramsByIds = useCallback( - (ids, fromScratch) => { + (ids: UUID[], fromScratch: boolean) => { if (ids?.length) { // we remove duplicates (because of NAD) let uniqueIds = ids.filter((id, index) => ids.indexOf(id) === index); @@ -673,16 +715,17 @@ export function DiagramPane({ studyUuid, showInSpreadsheet, currentNode, visible if (fromScratch) { updatedDiagramPromise = createView(currentView); } else { - updatedDiagramPromise = currentView.fetchSvg(); + updatedDiagramPromise = currentView.fetchSvg?.(); } - updatedDiagramPromise.then((svg) => { + updatedDiagramPromise?.then((svg) => { setViews((views) => { const updatedViews = views.slice(); - updatedViews[i] = { + const data: DiagramView = { ...updatedViews[i], ...svg, loadingState: false, - }; + } as unknown as DiagramView; + updatedViews[i] = data; if (fromScratch && svg.svgType === DiagramType.NETWORK_AREA_DIAGRAM) { dispatch( setNetworkAreaDiagramNbVoltageLevels(svg.additionalMetadata?.nbVoltageLevels) @@ -743,7 +786,7 @@ export function DiagramPane({ studyUuid, showInSpreadsheet, currentNode, visible */ // This function is called by the diagram's contents, when they get their sizes from the backend. - const setDiagramSize = (diagramId, diagramType, width, height) => { + const setDiagramSize = (diagramId: UUID, diagramType: DiagramType, width: number, height: number) => { // Let's update the stored values if they are new const storedValues = diagramContentSizes?.get(diagramType + diagramId); if (!storedValues || storedValues.width !== width || storedValues.height !== height) { @@ -756,7 +799,7 @@ export function DiagramPane({ studyUuid, showInSpreadsheet, currentNode, visible } }; - const getDefaultHeightByDiagramType = (diagramType) => { + const getDefaultHeightByDiagramType = (diagramType: DiagramType) => { switch (diagramType) { case DiagramType.SUBSTATION: return DEFAULT_HEIGHT_SUBSTATION; @@ -770,7 +813,7 @@ export function DiagramPane({ studyUuid, showInSpreadsheet, currentNode, visible } }; - const getDefaultWidthByDiagramType = (diagramType) => { + const getDefaultWidthByDiagramType = (diagramType: DiagramType) => { switch (diagramType) { case DiagramType.SUBSTATION: return DEFAULT_WIDTH_SUBSTATION; @@ -785,7 +828,7 @@ export function DiagramPane({ studyUuid, showInSpreadsheet, currentNode, visible }; const getDiagramOrDefaultHeight = useCallback( - (diagramId, diagramType) => { + (diagramId: UUID, diagramType: DiagramType) => { return ( diagramContentSizes.get(diagramType + diagramId)?.height ?? getDefaultHeightByDiagramType(diagramType) ); @@ -794,13 +837,13 @@ export function DiagramPane({ studyUuid, showInSpreadsheet, currentNode, visible ); const getDiagramOrDefaultWidth = useCallback( - (diagramId, diagramType) => { + (diagramId: UUID, diagramType: DiagramType) => { return diagramContentSizes.get(diagramType + diagramId)?.width ?? getDefaultWidthByDiagramType(diagramType); }, [diagramContentSizes] ); - const getRatioWidthByHeight = (width, height) => { + const getRatioWidthByHeight = (width: number, height: number) => { if (Number(height) > 0) { return Number(width) / Number(height); } @@ -813,7 +856,7 @@ export function DiagramPane({ studyUuid, showInSpreadsheet, currentNode, visible * diagram will have its own height. */ const getMaxHeightFromDisplayedDiagrams = useCallback( - (svgType) => { + (svgType: DiagramType) => { // First, we check which diagrams are displayed in the pane with a compatible svgType // and for which we stored a height in diagramContentSizes. const matchingDiagrams = displayedDiagrams @@ -828,7 +871,7 @@ export function DiagramPane({ studyUuid, showInSpreadsheet, currentNode, visible // Then, we find the maximum height from these diagrams if (matchingDiagrams.length > 0) { return matchingDiagrams.reduce( - (maxFoundHeight, currentDiagram) => + (maxFoundHeight: number, currentDiagram) => (maxFoundHeight || 1) > diagramContentSizes.get(currentDiagram.svgType + currentDiagram.id).height ? maxFoundHeight @@ -848,7 +891,7 @@ export function DiagramPane({ studyUuid, showInSpreadsheet, currentNode, visible * share their heights, whereas a network area diagram will have its own height). */ const getWidthForPaneDisplay = useCallback( - (diagramId, diagramType) => { + (diagramId: UUID, diagramType: DiagramType) => { const diagramWidth = getDiagramOrDefaultWidth(diagramId, diagramType); const diagramHeight = getDiagramOrDefaultHeight(diagramId, diagramType); @@ -863,7 +906,7 @@ export function DiagramPane({ studyUuid, showInSpreadsheet, currentNode, visible * the pane, and the other diagrams' sizes. */ const getHeightForPaneDisplay = useCallback( - (diagramType, availableWidth, availableHeight) => { + (diagramType: DiagramType, availableWidth: number, availableHeight: number) => { let result; const maxHeightFromDisplayedDiagrams = getMaxHeightFromDisplayedDiagrams(diagramType); @@ -908,7 +951,7 @@ export function DiagramPane({ studyUuid, showInSpreadsheet, currentNode, visible [displayedDiagrams, diagramContentSizes, getMaxHeightFromDisplayedDiagrams] ); - const getDiagramTitle = (diagramView) => { + const getDiagramTitle = (diagramView: DiagramView) => { return diagramView.svgType !== DiagramType.NETWORK_AREA_DIAGRAM ? diagramView.name + ' - ' + translate(diagramView.country) : diagramView.name; @@ -919,7 +962,7 @@ export function DiagramPane({ studyUuid, showInSpreadsheet, currentNode, visible */ const handleWarningToDisplay = useCallback( - (diagramView) => { + (diagramView: DiagramView) => { if (!isNodeBuilt(currentNode)) { return 'InvalidNode'; } @@ -970,7 +1013,6 @@ export function DiagramPane({ studyUuid, showInSpreadsheet, currentNode, visible {(diagramView.svgType === DiagramType.VOLTAGE_LEVEL || diagramView.svgType === DiagramType.SUBSTATION) && ( ); } - -DiagramPane.propTypes = { - studyUuid: PropTypes.string, - currentNode: PropTypes.object, - showInSpreadsheet: PropTypes.func, - oneBusShortCircuitStatus: PropTypes.string.isRequired, - visible: PropTypes.bool, -}; diff --git a/src/components/diagrams/diagram-resizable-box.jsx b/src/components/diagrams/diagram-resizable-box.tsx similarity index 62% rename from src/components/diagrams/diagram-resizable-box.jsx rename to src/components/diagrams/diagram-resizable-box.tsx index 341fb43820..fb72615894 100644 --- a/src/components/diagrams/diagram-resizable-box.jsx +++ b/src/components/diagrams/diagram-resizable-box.tsx @@ -7,17 +7,17 @@ import React from 'react'; import { ResizableBox } from 'react-resizable'; -import PropTypes from 'prop-types'; import ResizeHandleIcon from '@mui/icons-material/ChevronRight'; import { MIN_HEIGHT, MIN_WIDTH, LOADING_HEIGHT, LOADING_WIDTH } from './diagram-common'; import { mergeSx } from '../utils/functions'; import { styled } from '@mui/system'; +import { Theme } from '@mui/material'; // TODO can we avoid to define a component just to add sx support ? const ResizableBoxSx = styled(ResizableBox)({}); const styles = { - resizable: (theme) => ({ + resizable: (theme: Theme) => ({ position: 'relative', '& .react-resizable-handle': { position: 'absolute', @@ -38,14 +38,14 @@ const styles = { cursor: 'sw-resize', }, }, - resizeHandleIconRight: (theme) => ({ + resizeHandleIconRight: (theme: Theme) => ({ bottom: theme.spacing(-0.5), right: theme.spacing(-0.5), position: 'absolute', transform: 'rotate(45deg)', color: theme.palette.action.disabled, }), - resizeHandleIconLeft: (theme) => ({ + resizeHandleIconLeft: (theme: Theme) => ({ bottom: theme.spacing(-0.5), left: theme.spacing(-0.5), position: 'absolute', @@ -54,26 +54,42 @@ const styles = { }), }; -const DiagramResizableBox = (props) => { +interface DiagramResizableBoxProps { + children?: React.ReactNode; + disableResize?: boolean; + hide?: boolean; + align?: 'left' | 'right' | 'center'; + width?: number; + height?: number; +} + +const DiagramResizableBox: React.FC = ({ + children, + disableResize = false, + hide = false, + align = 'left', + width = LOADING_WIDTH, + height = LOADING_HEIGHT, +}) => { return ( <> - {props.children} - {!props.disableResize && ( + {children} + {!disableResize && ( )} @@ -81,21 +97,4 @@ const DiagramResizableBox = (props) => { ); }; -DiagramResizableBox.defaultProps = { - disableResize: false, - hide: false, - align: 'left', - width: LOADING_WIDTH, - height: LOADING_HEIGHT, -}; - -DiagramResizableBox.propTypes = { - children: PropTypes.node, - disableResize: PropTypes.bool, - hide: PropTypes.bool, - align: PropTypes.string, - width: PropTypes.number, - height: PropTypes.number, -}; - export default DiagramResizableBox; diff --git a/src/components/diagrams/diagram-utils.test.js b/src/components/diagrams/diagram-utils.test.ts similarity index 87% rename from src/components/diagrams/diagram-utils.test.js rename to src/components/diagrams/diagram-utils.test.ts index a2b01e89c7..d02e9b723d 100644 --- a/src/components/diagrams/diagram-utils.test.js +++ b/src/components/diagrams/diagram-utils.test.ts @@ -21,14 +21,14 @@ test('diagram-common.sortDiagrams', () => { ].sort(makeDiagramSorter(diagramStates)); expect(table.length).toBe(7); - expect(table[0].align).toBe('left'); - expect(table[0].id).toBe(3); - expect(table[1].align).toBe('left'); - expect(table[1].id).toBe(2); - expect(table[2].align).toBe('right'); - expect(table[2].id).toBe(4); - expect(table[3].align).toBe('right'); - expect(table[3].id).toBe(1); + expect(table[0]!.align).toBe('left'); + expect(table[0]!.id).toBe(3); + expect(table[1]!.align).toBe('left'); + expect(table[1]!.id).toBe(2); + expect(table[2]!.align).toBe('right'); + expect(table[2]!.id).toBe(4); + expect(table[3]!.align).toBe('right'); + expect(table[3]!.id).toBe(1); expect(table[4]?.align).not.toBe('left'); expect(table[4]?.align).not.toBe('right'); expect(table[5]?.align).not.toBe('left'); diff --git a/src/components/diagrams/diagram-utils.js b/src/components/diagrams/diagram-utils.ts similarity index 79% rename from src/components/diagrams/diagram-utils.js rename to src/components/diagrams/diagram-utils.ts index fceb4b0924..e43aece79b 100644 --- a/src/components/diagrams/diagram-utils.js +++ b/src/components/diagrams/diagram-utils.ts @@ -5,13 +5,16 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +import { DiagramState } from '../../redux/reducer'; import { DiagramType } from './diagram-common'; /** * SORTING FUNCTIONS */ -const innerSortByAlign = (align) => { +type DiagramAlignment = 'left' | 'right' | undefined; + +const innerSortByAlign = (align: DiagramAlignment): number => { if (align === 'left') { return 10; } @@ -24,7 +27,7 @@ const innerSortByAlign = (align) => { /* * Sorts by the object's "align" parameter. Values equal to "left" will be before "right" values, and others or undefined will be last. */ -const sortByAlign = (a, b) => { +const sortByAlign = (a: { align: DiagramAlignment }, b: { align: DiagramAlignment }) => { return innerSortByAlign(a?.align) - innerSortByAlign(b?.align); }; @@ -33,7 +36,7 @@ const sortByAlign = (a, b) => { * So we keep the same order as in the redux store. * We use the ID and type of the objects to identify their indexes. */ -const sortByIndex = (a, b, diagramStates) => { +const sortByIndex = (a: any, b: any, diagramStates: any[]) => { return ( diagramStates.findIndex((diagramState) => diagramState.id === a?.id && diagramState.svgType === a?.svgType) - diagramStates.findIndex((diagramState) => diagramState.id === b?.id && diagramState.svgType === b?.svgType) @@ -46,7 +49,8 @@ const sortByIndex = (a, b, diagramStates) => { * @param initNadWithGeoData config parameter specifying if the nad uses geographical data * @returns {string} */ -export const getNadIdentifier = (diagramStates, initNadWithGeoData) => { + +export const getNadIdentifier = (diagramStates: DiagramState[], initNadWithGeoData: boolean): string => { const result = diagramStates .filter((diagram) => diagram.svgType === DiagramType.NETWORK_AREA_DIAGRAM) @@ -63,8 +67,8 @@ export const getNadIdentifier = (diagramStates, initNadWithGeoData) => { * @param diagramStates the diagrams array of the redux store * @returns {(function(*, *): (*))|*} new array sorting function based on diagramStates */ -export const makeDiagramSorter = (diagramStates) => { - return (a, b) => sortByAlign(a, b) || sortByIndex(a, b, diagramStates); +export const makeDiagramSorter = (diagramStates: any[]): ((a: any, b: any) => number) => { + return (a: any, b: any): number => sortByAlign(a, b) || sortByIndex(a, b, diagramStates); }; // estimate the number of voltage levels for a requested depth @@ -75,7 +79,11 @@ export const makeDiagramSorter = (diagramStates) => { // we want this estimation to be slightly pessimistic to avoid bad UX of going to far // and not being able to do the same thing step by step. const VL_DEPTH_GROWTH_RATE = 2; -export function getEstimatedNbVoltageLevels(currentDepth, requestedDepth, previousVoltagesNB) { +export function getEstimatedNbVoltageLevels( + currentDepth: number, + requestedDepth: number, + previousVoltagesNB: number +): number { // We assume that the number of vl grows exponentially // real world example : // depth : number of voltage levels diff --git a/src/components/diagrams/diagram.jsx b/src/components/diagrams/diagram.tsx similarity index 59% rename from src/components/diagrams/diagram.jsx rename to src/components/diagrams/diagram.tsx index dcbe5737f6..757ff73d21 100644 --- a/src/components/diagrams/diagram.jsx +++ b/src/components/diagrams/diagram.tsx @@ -5,8 +5,6 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -import React from 'react'; -import PropTypes from 'prop-types'; import { useDispatch, useSelector } from 'react-redux'; import Box from '@mui/material/Box'; import Paper from '@mui/material/Paper'; @@ -30,53 +28,81 @@ import DiagramHeader from './diagram-header'; import DiagramFooter from './diagram-footer'; import DiagramResizableBox from './diagram-resizable-box'; import AlertCustomMessageNode from '../utils/alert-custom-message-node'; - -const Diagram = (props) => { +import { AppState } from 'redux/reducer'; + +interface DiagramProps { + align?: 'left' | 'right' | 'center'; + diagramId: string; + diagramTitle: string; + warningToDisplay?: string; + pinned?: boolean; + svgType: DiagramType; + children?: React.ReactNode; + width?: number; + height?: number; + fullscreenWidth?: number; + fullscreenHeight?: number; + loadingState?: boolean; +} + +const Diagram: React.FC = ({ + align = 'left', + diagramId, + diagramTitle, + warningToDisplay = '', + pinned = false, + svgType, + children, + width = LOADING_WIDTH, + height = LOADING_HEIGHT, + fullscreenWidth = LOADING_WIDTH, + fullscreenHeight = LOADING_HEIGHT, + loadingState, +}) => { const dispatch = useDispatch(); const intl = useIntl(); const { minimizeDiagramView, togglePinDiagramView, closeDiagramView } = useDiagram(); - const fullScreenDiagram = useSelector((state) => state.fullScreenDiagram); + const fullScreenDiagram = useSelector((state: AppState) => state.fullScreenDiagram); - const shouldBeHidden = - fullScreenDiagram?.id && - (fullScreenDiagram.id !== props.diagramId || fullScreenDiagram.svgType !== props.svgType); + const shouldBeHidden: boolean = + Boolean(fullScreenDiagram?.id) && + (fullScreenDiagram?.id !== diagramId || fullScreenDiagram?.svgType !== svgType); - const shouldBeFullscreen = - fullScreenDiagram?.id === props.diagramId && fullScreenDiagram?.svgType === props.svgType; + const shouldBeFullscreen: boolean = fullScreenDiagram?.id === diagramId && fullScreenDiagram?.svgType === svgType; - const networkAreaDiagramDepth = useSelector((state) => state.networkAreaDiagramDepth); + const networkAreaDiagramDepth = useSelector((state: AppState) => state.networkAreaDiagramDepth); - const nbVoltageLevels = useSelector((state) => state.networkAreaDiagramNbVoltageLevels); + const nbVoltageLevels = useSelector((state: AppState) => state.networkAreaDiagramNbVoltageLevels); - const incrementCounterDisabled = props.loadingState || nbVoltageLevels > NETWORK_AREA_DIAGRAM_NB_MAX_VOLTAGE_LEVELS; + const incrementCounterDisabled = loadingState || nbVoltageLevels > NETWORK_AREA_DIAGRAM_NB_MAX_VOLTAGE_LEVELS; - const decrementCounterDisabled = props.loadingState || networkAreaDiagramDepth === 0; + const decrementCounterDisabled = loadingState || networkAreaDiagramDepth === 0; /** * DIAGRAM CONTROL HANDLERS */ const onMinimizeHandler = () => { - minimizeDiagramView(props.diagramId, props.svgType); + minimizeDiagramView(diagramId, svgType); dispatch(setFullScreenDiagram(null)); }; const onTogglePinHandler = () => { - togglePinDiagramView(props.diagramId, props.svgType); + togglePinDiagramView(diagramId, svgType); }; const onCloseHandler = () => { dispatch(setFullScreenDiagram(null)); - closeDiagramView(props.diagramId, props.svgType); - if (props.svgType === DiagramType.NETWORK_AREA_DIAGRAM) { + closeDiagramView(diagramId, svgType); + if (svgType === DiagramType.NETWORK_AREA_DIAGRAM) { dispatch(resetNetworkAreaDiagramDepth()); } }; const onShowFullScreenHandler = () => { - dispatch(setFullScreenDiagram(props.diagramId, props.svgType)); + dispatch(setFullScreenDiagram(diagramId, svgType)); }; const onHideFullScreenHandler = () => { @@ -97,9 +123,9 @@ const Diagram = (props) => { return ( { }} > - {props.warningToDisplay ? ( - + {warningToDisplay ? ( + ) : ( - <>{props.children} + <>{children} )} { ); }; -Diagram.defaultProps = { - pinned: false, - warningToDisplay: '', - align: 'left', - width: LOADING_WIDTH, - height: LOADING_HEIGHT, - fullscreenWidth: LOADING_WIDTH, - fullscreenHeight: LOADING_HEIGHT, -}; - -Diagram.propTypes = { - align: PropTypes.string, - diagramId: PropTypes.string, - diagramTitle: PropTypes.string.isRequired, - warningToDisplay: PropTypes.string, - pinned: PropTypes.bool, - svgType: PropTypes.string.isRequired, - children: PropTypes.node, - width: PropTypes.number, - height: PropTypes.number, - fullscreenWidth: PropTypes.number, - fullscreenHeight: PropTypes.number, - loadingState: PropTypes.bool, -}; - export default Diagram; diff --git a/src/components/diagrams/networkAreaDiagram/network-area-diagram-content.jsx b/src/components/diagrams/networkAreaDiagram/network-area-diagram-content.tsx similarity index 83% rename from src/components/diagrams/networkAreaDiagram/network-area-diagram-content.jsx rename to src/components/diagrams/networkAreaDiagram/network-area-diagram-content.tsx index 3ba09973d9..505ed29c4d 100644 --- a/src/components/diagrams/networkAreaDiagram/network-area-diagram-content.jsx +++ b/src/components/diagrams/networkAreaDiagram/network-area-diagram-content.tsx @@ -7,7 +7,6 @@ import React, { useLayoutEffect, useRef, useMemo, useCallback } from 'react'; import { useDispatch, useSelector } from 'react-redux'; -import PropTypes from 'prop-types'; import { RunningStatus } from '../../utils/running-status'; import { MIN_HEIGHT, @@ -15,17 +14,20 @@ import { MAX_HEIGHT_NETWORK_AREA_DIAGRAM, MAX_WIDTH_NETWORK_AREA_DIAGRAM, styles, + DiagramType, } from '../diagram-common'; -import { NetworkAreaDiagramViewer, THRESHOLD_STATUS } from '@powsybl/diagram-viewer'; +import { CSS_RULE, NetworkAreaDiagramViewer, THRESHOLD_STATUS } from '@powsybl/diagram-viewer'; import LinearProgress from '@mui/material/LinearProgress'; import Box from '@mui/material/Box'; import { mergeSx } from '../../utils/functions'; -import ComputingType from 'components/computing-status/computing-type'; +import ComputingType from '../../computing-status/computing-type'; +import { AppState } from 'redux/reducer'; import { storeNetworkAreaDiagramNodeMovement } from '../../../redux/actions'; import { PARAM_INIT_NAD_WITH_GEO_DATA } from '../../../utils/config-params.js'; import { getNadIdentifier } from '../diagram-utils.js'; +import { UUID } from 'crypto'; -const dynamicCssRules = [ +const dynamicCssRules: CSS_RULE[] = [ { cssSelector: '.nad-edge-infos', // data on edges (arrows and values) belowThresholdCssDeclaration: { display: 'block' }, @@ -119,23 +121,31 @@ const dynamicCssRules = [ }, ]; -function NetworkAreaDiagramContent(props) { +type NetworkAreaDiagramContentProps = { + readonly svgType: DiagramType; + readonly svg?: string; + readonly loadingState: boolean; + readonly diagramSizeSetter: (id: UUID, type: DiagramType, width: number, height: number) => void; + readonly diagramId: UUID; +}; +function NetworkAreaDiagramContent(props: NetworkAreaDiagramContentProps) { const { diagramSizeSetter } = props; const dispatch = useDispatch(); const svgRef = useRef(); - const diagramViewerRef = useRef(); - const currentNode = useSelector((state) => state.currentTreeNode); - const loadFlowStatus = useSelector((state) => state.computingStatus[ComputingType.LOAD_FLOW]); - const nadNodeMovements = useSelector((state) => state.nadNodeMovements); - const diagramStates = useSelector((state) => state.diagramStates); - const initNadWithGeoData = useSelector((state) => state[PARAM_INIT_NAD_WITH_GEO_DATA]); + + const diagramViewerRef = useRef(); + const currentNode = useSelector((state: AppState) => state.currentTreeNode); + const loadFlowStatus = useSelector((state: AppState) => state.computingStatus[ComputingType.LOAD_FLOW]); + const nadNodeMovements = useSelector((state: AppState) => state.nadNodeMovements); + const diagramStates = useSelector((state: AppState) => state.diagramStates); + const initNadWithGeoData = useSelector((state: AppState) => state[PARAM_INIT_NAD_WITH_GEO_DATA]); const nadIdentifier = useMemo(() => { return getNadIdentifier(diagramStates, initNadWithGeoData); }, [diagramStates, initNadWithGeoData]); const onMoveNodeCallback = useCallback( - (equipmentId, nodeId, x, y, xOrig, yOrig) => { + (equipmentId: string, nodeId: string, x: number, y: number, xOrig: number, yOrig: number) => { dispatch(storeNetworkAreaDiagramNodeMovement(nadIdentifier, equipmentId, x, y)); }, [dispatch, nadIdentifier] @@ -146,7 +156,7 @@ function NetworkAreaDiagramContent(props) { */ useLayoutEffect(() => { - if (props.svg) { + if (props.svg && svgRef.current) { const diagramViewer = new NetworkAreaDiagramViewer( svgRef.current, props.svg, @@ -172,7 +182,10 @@ function NetworkAreaDiagramContent(props) { diagramViewer.getWidth() === diagramViewerRef.current.getWidth() && diagramViewer.getHeight() === diagramViewerRef.current.getHeight() ) { - diagramViewer.setViewBox(diagramViewerRef.current.getViewBox()); + const viewBox = diagramViewerRef.current.getViewBox(); + if (viewBox) { + diagramViewer.setViewBox(viewBox); + } } // Repositioning the previously moved nodes @@ -216,12 +229,4 @@ function NetworkAreaDiagramContent(props) { ); } -NetworkAreaDiagramContent.propTypes = { - svgType: PropTypes.string, - svg: PropTypes.string, - loadingState: PropTypes.bool, - diagramSizeSetter: PropTypes.func, - diagramId: PropTypes.string, -}; - export default NetworkAreaDiagramContent; diff --git a/src/components/diagrams/singleLineDiagram/position-diagram-pane.jsx b/src/components/diagrams/singleLineDiagram/position-diagram-pane.tsx similarity index 70% rename from src/components/diagrams/singleLineDiagram/position-diagram-pane.jsx rename to src/components/diagrams/singleLineDiagram/position-diagram-pane.tsx index 47a3f0eb01..f5202d284e 100644 --- a/src/components/diagrams/singleLineDiagram/position-diagram-pane.jsx +++ b/src/components/diagrams/singleLineDiagram/position-diagram-pane.tsx @@ -19,15 +19,32 @@ import PositionDiagram from './position-diagram'; import { SLD_DISPLAY_MODE } from '../../network/constants'; import { DiagramType } from '../diagram-common'; import { getVoltageLevelSingleLineDiagram } from '../../../services/study/network'; +import { FC } from 'react'; +import { AppState } from 'redux/reducer'; +import { UUID } from 'crypto'; -const PositionDiagramPane = ({ open, onClose, voltageLevelId, currentNodeUuid, studyUuid }) => { - const useName = useSelector((state) => state[PARAM_USE_NAME]); - const centerName = useSelector((state) => state[PARAM_CENTER_LABEL]); - const diagonalName = useSelector((state) => state[PARAM_DIAGONAL_LABEL]); - const componentLibrary = useSelector((state) => state[PARAM_COMPONENT_LIBRARY]); - const language = useSelector((state) => state[PARAM_LANGUAGE]); +interface PositionDiagramPaneProps { + open: boolean; + onClose: () => void; + voltageLevelId?: { id: UUID }; + currentNodeUuid: UUID; + studyUuid: UUID; +} - const [svgUrl, setSvgUrl] = useState(null); +const PositionDiagramPane: FC = ({ + open, + onClose, + voltageLevelId, + currentNodeUuid, + studyUuid, +}) => { + const useName = useSelector((state: AppState) => state[PARAM_USE_NAME]); + const centerName = useSelector((state: AppState) => state[PARAM_CENTER_LABEL]); + const diagonalName = useSelector((state: AppState) => state[PARAM_DIAGONAL_LABEL]); + const componentLibrary = useSelector((state: AppState) => state[PARAM_COMPONENT_LIBRARY]); + const language = useSelector((state: AppState) => state[PARAM_LANGUAGE]); + + const [svgUrl, setSvgUrl] = useState(null); const handleClose = () => { setSvgUrl(null); onClose(); diff --git a/src/components/diagrams/singleLineDiagram/position-diagram.jsx b/src/components/diagrams/singleLineDiagram/position-diagram.tsx similarity index 74% rename from src/components/diagrams/singleLineDiagram/position-diagram.jsx rename to src/components/diagrams/singleLineDiagram/position-diagram.tsx index 2a68e64620..cd24b9012e 100644 --- a/src/components/diagrams/singleLineDiagram/position-diagram.jsx +++ b/src/components/diagrams/singleLineDiagram/position-diagram.tsx @@ -5,34 +5,47 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -import React, { forwardRef, useEffect, useLayoutEffect, useRef, useState } from 'react'; -import PropTypes from 'prop-types'; - +import React, { forwardRef, useEffect, useLayoutEffect, useRef, useState, Ref } from 'react'; import { useSelector } from 'react-redux'; import Box from '@mui/material/Box'; import { useTheme } from '@mui/material/styles'; import LinearProgress from '@mui/material/LinearProgress'; import { SingleLineDiagramViewer } from '@powsybl/diagram-viewer'; -import { styles, MAX_HEIGHT_VOLTAGE_LEVEL, MAX_WIDTH_VOLTAGE_LEVEL, NoSvg, MIN_WIDTH } from '../diagram-common'; +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 { Paper } from '@mui/material'; import DiagramHeader from '../diagram-header'; import { fetchSvg } from '../../../services/study'; import { mergeSx } from '../../utils/functions'; - -const PositionDiagram = forwardRef((props, ref) => { - const [svg, setSvg] = useState(NoSvg); +import { AppState } from 'redux/reducer'; + +interface PositionDiagramProps { + diagramTitle: string; + svgUrl: string | null; + onClose: () => void; + svgType: string; + disabled?: boolean; +} + +const PositionDiagram = forwardRef((props: PositionDiagramProps, ref: Ref) => { + const [svg, setSvg] = useState({ + svg: null, + metadata: null, + additionalMetadata: null, + error: null, + svgUrl: null, + }); const svgUrl = useRef(''); - const svgDraw = useRef(); + const svgDraw = useRef(null); const { snackError } = useSnackMessage(); const intlRef = useIntlRef(); - const svgRef = useRef(); + const svgRef = useRef(); const { svgType, disabled } = props; - const currentNode = useSelector((state) => state.currentTreeNode); + const currentNode = useSelector((state: AppState) => state.currentTreeNode); - const [loadingState, updateLoadingState] = useState(false); + const [loadingState, updateLoadingState] = useState(false); const theme = useTheme(); @@ -50,6 +63,7 @@ const PositionDiagram = forwardRef((props, ref) => { setSvg({ svg: data.svg, metadata: data.metadata, + additionalMetadata: data.additionalMetadata, error: null, svgUrl: props.svgUrl, }); @@ -63,6 +77,7 @@ const PositionDiagram = forwardRef((props, ref) => { setSvg({ svg: null, metadata: null, + additionalMetadata: null, error: errorMessage, svgUrl: props.svgUrl, }); @@ -85,9 +100,13 @@ const PositionDiagram = forwardRef((props, ref) => { let viewboxMaxWidth = MAX_WIDTH_VOLTAGE_LEVEL; let viewboxMaxHeight = MAX_HEIGHT_VOLTAGE_LEVEL; let selectionBackColor = theme.palette.background.paper; + const container = svgRef.current; + if (!container) { + return; + } const sldViewer = new SingleLineDiagramViewer( - svgRef.current, //container + container, //container svg.svg, //svgContent svg.metadata, //svg metadata svgType, @@ -95,16 +114,25 @@ const PositionDiagram = forwardRef((props, ref) => { 0, viewboxMaxWidth, viewboxMaxHeight, - selectionBackColor //arrows color + null, + null, + null, + null, + selectionBackColor, //arrows color + null ); setServerHeight(sldViewer.getHeight()); setServerWidth(sldViewer.getWidth()); - if (svgDraw.current && svgUrl.current === svg.svgUrl) { - sldViewer.setViewBox(svgDraw.current.getViewBox()); + const viewBox = svgDraw.current.getViewBox(); + if (viewBox) { + sldViewer.setViewBox(viewBox); + } + } + if (svg.svgUrl) { + svgUrl.current = svg.svgUrl; } - svgUrl.current = svg.svgUrl; svgDraw.current = sldViewer; } }, [svg, currentNode, svgType, theme, ref, disabled, loadingState]); @@ -113,10 +141,10 @@ const PositionDiagram = forwardRef((props, ref) => { if (serverWidth && serverHeight) { const divElt = svgRef.current; if (divElt != null) { - const svgEl = divElt.getElementsByTagName('svg')[0]; + const svgEl = divElt.getElementsByTagName('svg')[0] as SVGElement; if (svgEl != null) { - svgEl.setAttribute('width', serverWidth); - svgEl.setAttribute('height', serverHeight); + svgEl.setAttribute('width', serverWidth.toString()); + svgEl.setAttribute('height', serverHeight.toString()); } } } @@ -160,12 +188,4 @@ const PositionDiagram = forwardRef((props, ref) => { ); }); -PositionDiagram.propTypes = { - diagramTitle: PropTypes.string.isRequired, - svgUrl: PropTypes.string, - onClose: PropTypes.func, - svgType: PropTypes.string.isRequired, - disabled: PropTypes.bool, -}; - export default PositionDiagram; diff --git a/src/components/diagrams/singleLineDiagram/single-line-diagram-content.jsx b/src/components/diagrams/singleLineDiagram/single-line-diagram-content.tsx similarity index 79% rename from src/components/diagrams/singleLineDiagram/single-line-diagram-content.jsx rename to src/components/diagrams/singleLineDiagram/single-line-diagram-content.tsx index d7de91bee2..ebb5c0af6f 100644 --- a/src/components/diagrams/singleLineDiagram/single-line-diagram-content.jsx +++ b/src/components/diagrams/singleLineDiagram/single-line-diagram-content.tsx @@ -7,7 +7,6 @@ import { useCallback, useLayoutEffect, useRef, useState } from 'react'; import { useDispatch, useSelector } from 'react-redux'; -import PropTypes from 'prop-types'; import { RunningStatus } from '../../utils/running-status'; import { DiagramType, @@ -24,7 +23,7 @@ import { import withEquipmentMenu from '../../menus/equipment-menu'; import BaseEquipmentMenu from '../../menus/base-equipment-menu'; import withOperatingStatusMenu from '../../menus/operating-status-menu'; -import { SingleLineDiagramViewer } from '@powsybl/diagram-viewer'; +import { OnBreakerCallbackType, SingleLineDiagramViewer, SLDMetadata } from '@powsybl/diagram-viewer'; import { isNodeReadOnly } from '../../graph/util/model-functions'; import { useIsAnyNodeBuilding } from '../../utils/is-any-node-building-hook'; import Alert from '@mui/material/Alert'; @@ -52,40 +51,88 @@ 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 } from '../../../redux/actions'; +import { AppState } from 'redux/reducer'; +import { UUID } from 'crypto'; + +type EquipmentMenuState = { + position: [number, number]; + equipmentId: string | null; + equipmentType: EQUIPMENT_TYPES | null; + svgId: string | null; + display: boolean; +}; +interface SingleLineDiagramContentProps { + readonly showInSpreadsheet: (menu: { equipmentId: string | null; type: string | null }) => void; + readonly studyUuid: string; + readonly svgType: DiagramType; + readonly svg?: string; + readonly svgMetadata?: SLDMetadata; + readonly loadingState: boolean; + readonly diagramSizeSetter: (id: UUID, type: DiagramType, width: number, height: number) => void; + readonly diagramId: UUID; +} + +type EquipmentToModify = { + equipmentId: string; + equipmentType: EQUIPMENT_TYPES; +}; + +const defaultMenuState: EquipmentMenuState = { + position: [-1, -1], + equipmentId: null, + equipmentType: null, + svgId: null, + display: false, +}; -function SingleLineDiagramContent(props) { +type BusMenuState = { + position: [number, number]; + busId: string | null; + svgId: string | null; + display: boolean; +}; + +const defaultBusMenuState: BusMenuState = { + position: [-1, -1], + busId: null, + svgId: null, + display: false, +}; + +function SingleLineDiagramContent(props: SingleLineDiagramContentProps) { const { diagramSizeSetter, studyUuid } = props; const theme = useTheme(); const dispatch = useDispatch(); const MenuBranch = withOperatingStatusMenu(BaseEquipmentMenu); - const svgRef = useRef(); - const diagramViewerRef = useRef(); + const svgRef = useRef(); + const diagramViewerRef = useRef(); const { snackError } = useSnackMessage(); - const currentNode = useSelector((state) => state.currentTreeNode); + const currentNode = useSelector((state: AppState) => state.currentTreeNode); const [modificationInProgress, setModificationInProgress] = useState(false); const isAnyNodeBuilding = useIsAnyNodeBuilding(); - const [locallySwitchedBreaker, setLocallySwitchedBreaker] = useState(); + const [locallySwitchedBreaker, setLocallySwitchedBreaker] = useState(); const [errorMessage, setErrorMessage] = useState(''); const { openDiagramView } = useDiagram(); - const [equipmentToModify, setEquipmentToModify] = useState(); - const [equipmentToDelete, setEquipmentToDelete] = useState(); + const [equipmentToModify, setEquipmentToModify] = useState(); + const [equipmentToDelete, setEquipmentToDelete] = useState(); const [shouldDisplayTooltip, setShouldDisplayTooltip] = useState(false); - const [equipmentPopoverAnchorEl, setEquipmentPopoverAnchorEl] = useState(null); + const [equipmentPopoverAnchorEl, setEquipmentPopoverAnchorEl] = useState(null); const [hoveredEquipmentId, setHoveredEquipmentId] = useState(''); const [hoveredEquipmentType, setHoveredEquipmentType] = useState(''); const [enableDeveloperMode] = useParameterState(PARAM_DEVELOPER_MODE); - const computationStarting = useSelector((state) => state.computationStarting); - const loadFlowStatus = useSelector((state) => state.computingStatus[ComputingType.LOAD_FLOW]); + const computationStarting = useSelector((state: AppState) => state.computationStarting); + const loadFlowStatus = useSelector((state: AppState) => state.computingStatus[ComputingType.LOAD_FLOW]); const [ oneBusShortcircuitAnalysisLoaderMessage, isDiagramRunningOneBusShortcircuitAnalysis, displayOneBusShortcircuitAnalysisLoader, resetOneBusShortcircuitAnalysisLoader, - ] = useOneBusShortcircuitAnalysisLoader(props.diagramId, currentNode.id); + ] = useOneBusShortcircuitAnalysisLoader(props.diagramId, currentNode?.id!); // dynamic simulation event configuration states - const [equipmentToConfigDynamicSimulationEvent, setEquipmentToConfigDynamicSimulationEvent] = useState(); + const [equipmentToConfigDynamicSimulationEvent, setEquipmentToConfigDynamicSimulationEvent] = + useState(); const [dynamicSimulationEventDialogTitle, setDynamicSimulationEventDialogTitle] = useState(''); /** @@ -94,17 +141,18 @@ function SingleLineDiagramContent(props) { const closeEquipmentMenu = useCallback(() => { setEquipmentMenu({ + ...defaultMenuState, display: false, }); }, []); - const handleOpenModificationDialog = (equipmentId, equipmentType) => { + const handleOpenModificationDialog = (equipmentId: string, equipmentType: EQUIPMENT_TYPES) => { closeEquipmentMenu(); setEquipmentToModify({ equipmentId, equipmentType }); }; const handleOpenDeletionDialog = useCallback( - (equipmentId, equipmentType) => { + (equipmentId: string, equipmentType: EQUIPMENT_TYPES) => { closeEquipmentMenu(); setEquipmentToDelete({ equipmentId, equipmentType }); }, @@ -112,15 +160,14 @@ function SingleLineDiagramContent(props) { ); const closeModificationDialog = () => { - setEquipmentToModify(); + setEquipmentToModify(undefined); }; const closeDeletionDialog = () => { - setEquipmentToDelete(); + setEquipmentToDelete(undefined); }; - const handleTogglePopover = useCallback( - (shouldDisplay, currentTarget, equipmentId, equipmentType) => { + (shouldDisplay: boolean, currentTarget: EventTarget | null, equipmentId: string, equipmentType: string) => { setShouldDisplayTooltip(shouldDisplay); if (shouldDisplay) { setHoveredEquipmentId(equipmentId); @@ -135,11 +182,12 @@ function SingleLineDiagramContent(props) { [setShouldDisplayTooltip] ); - const handleBreakerClick = useCallback( - (breakerId, newSwitchState, switchElement) => { + const handleBreakerClick: OnBreakerCallbackType = useCallback( + // switchElement should be SVGElement, this will be fixed once https://github.com/powsybl/powsybl-diagram-viewer/pull/106/ is merged + (breakerId, newSwitchState, switchElement: any) => { if (!modificationInProgress) { setModificationInProgress(true); - setLocallySwitchedBreaker(switchElement); + setLocallySwitchedBreaker(switchElement?.id); updateSwitchState(studyUuid, currentNode?.id, breakerId, newSwitchState).catch((error) => { console.error(error.message); @@ -151,7 +199,7 @@ function SingleLineDiagramContent(props) { ); const handleNextVoltageLevelClick = useCallback( - (id) => { + (id: string) => { // This function is called by powsybl-diagram-viewer when clicking on a navigation arrow in a single line diagram. // At the moment, there is no plan to open something other than a voltage-level by using these navigation arrows. if (!studyUuid || !currentNode) { @@ -162,24 +210,13 @@ function SingleLineDiagramContent(props) { [studyUuid, currentNode, openDiagramView] ); - const [equipmentMenu, setEquipmentMenu] = useState({ - position: [-1, -1], - equipmentId: null, - equipmentType: null, - svgId: null, - display: null, - }); - - const [busMenu, setBusMenu] = useState({ - position: [-1, -1], - busId: null, - svgId: null, - display: null, - }); + const [equipmentMenu, setEquipmentMenu] = useState(defaultMenuState); + + const [busMenu, setBusMenu] = useState(defaultBusMenuState); const showBusMenu = useCallback( - (busId, svgId, x, y) => { - handleTogglePopover(false, null, null); + (busId: string, svgId: string, x: number, y: number) => { + handleTogglePopover(false, null, '', ''); setBusMenu({ position: [x, y], busId: busId, @@ -189,10 +226,9 @@ function SingleLineDiagramContent(props) { }, [setBusMenu, handleTogglePopover] ); - const showEquipmentMenu = useCallback( - (equipmentId, equipmentType, svgId, x, y) => { - handleTogglePopover(false, null, null); + (equipmentId: string, equipmentType: string | null, svgId: string, x: number, y: number) => { + handleTogglePopover(false, null, '', ''); setEquipmentMenu({ position: [x, y], equipmentId: equipmentId, @@ -203,20 +239,22 @@ function SingleLineDiagramContent(props) { }, [handleTogglePopover] ); - const closeBusMenu = useCallback(() => { setBusMenu({ + position: [-1, -1], + busId: null, + svgId: null, display: false, }); }, []); const handleViewInSpreadsheet = () => { - props.showInSpreadsheet(equipmentMenu); + props.showInSpreadsheet({ equipmentId: equipmentMenu.equipmentId, type: equipmentMenu.equipmentType }); closeEquipmentMenu(); }; const removeEquipment = useCallback( - (equipmentType, equipmentId) => { + (equipmentType: string, equipmentId: string) => { deleteEquipment(studyUuid, currentNode?.id, equipmentType, equipmentId, undefined).catch((error) => { snackError({ messageTxt: error.message, @@ -229,7 +267,7 @@ function SingleLineDiagramContent(props) { ); const handleRunShortcircuitAnalysis = useCallback( - (busId) => { + (busId: string) => { dispatch(setComputingStatus(ComputingType.SHORT_CIRCUIT_ONE_BUS, RunningStatus.RUNNING)); displayOneBusShortcircuitAnalysisLoader(); dispatch(setComputationStarting(true)); @@ -260,16 +298,17 @@ function SingleLineDiagramContent(props) { {}} /> ) ); }; const handleDeleteEquipment = useCallback( - (equipmentType, equipmentId) => { + (equipmentType: EQUIPMENT_TYPES, equipmentId: string) => { if (equipmentType !== EQUIPMENT_TYPES.HVDC_LINE) { removeEquipment(equipmentType, equipmentId); } else { @@ -301,13 +340,16 @@ function SingleLineDiagramContent(props) { [studyUuid, currentNode?.id, snackError, handleOpenDeletionDialog, removeEquipment] ); - const handleOpenDynamicSimulationEventDialog = useCallback((equipmentId, equipmentType, dialogTitle) => { - setDynamicSimulationEventDialogTitle(dialogTitle); - setEquipmentToConfigDynamicSimulationEvent({ - equipmentId, - equipmentType, - }); - }, []); + const handleOpenDynamicSimulationEventDialog = useCallback( + (equipmentId: string, equipmentType: EQUIPMENT_TYPES, dialogTitle: string) => { + setDynamicSimulationEventDialogTitle(dialogTitle); + setEquipmentToConfigDynamicSimulationEvent({ + equipmentId, + equipmentType, + }); + }, + [] + ); const handleCloseDynamicSimulationEventDialog = useCallback(() => { setEquipmentToConfigDynamicSimulationEvent(undefined); @@ -316,6 +358,7 @@ function SingleLineDiagramContent(props) { const displayBranchMenu = () => { return ( equipmentMenu.display && + equipmentMenu.equipmentType && [ EQUIPMENT_TYPES.LINE, EQUIPMENT_TYPES.TWO_WINDINGS_TRANSFORMER, @@ -340,7 +383,7 @@ function SingleLineDiagramContent(props) { ); }; - const displayMenu = (equipmentType, menuId) => { + const displayMenu = (equipmentType: EQUIPMENT_TYPES, menuId: string) => { const Menu = withEquipmentMenu(BaseEquipmentMenu, menuId, equipmentType); return ( equipmentMenu.display && @@ -372,7 +415,7 @@ function SingleLineDiagramContent(props) { const displayModificationDialog = () => { let CurrentModificationDialog; - switch (equipmentToModify.equipmentType) { + switch (equipmentToModify?.equipmentType) { case EQUIPMENT_TYPES.BATTERY: CurrentModificationDialog = BatteryModificationDialog; break; @@ -402,12 +445,14 @@ function SingleLineDiagramContent(props) { defaultIdValue={equipmentToModify?.equipmentId} isUpdate={true} onClose={() => closeModificationDialog()} + editData={undefined} + editDataFetchStatus={undefined} /> ); }; const displayDeletionDialog = () => { - switch (equipmentToDelete.equipmentType) { + switch (equipmentToDelete?.equipmentType) { case EQUIPMENT_TYPES.HVDC_LINE: return ( closeDeletionDialog()} + editData={undefined} + editDataFetchStatus={undefined} /> ); default: @@ -429,14 +476,14 @@ function SingleLineDiagramContent(props) { */ useLayoutEffect(() => { - if (props.svg) { + if (props.svg && svgRef.current) { const isReadyForInteraction = !computationStarting && !isAnyNodeBuilding && !modificationInProgress && !props.loadingState; const diagramViewer = new SingleLineDiagramViewer( svgRef.current, //container props.svg, //svgContent - props.svgMetadata, //svg metadata + props.svgMetadata ?? null, //svg metadata props.svgType, //svg type MIN_WIDTH, // minWidth MIN_HEIGHT, // minHeight @@ -470,11 +517,11 @@ function SingleLineDiagramContent(props) { diagramSizeSetter(props.diagramId, props.svgType, diagramViewer.getWidth(), diagramViewer.getHeight()); // Rotate clicked switch while waiting for updated sld data - if (locallySwitchedBreaker?.id) { - const breakerToSwitchDom = document.getElementById(locallySwitchedBreaker.id); - if (breakerToSwitchDom.classList.value.includes('sld-closed')) { + if (locallySwitchedBreaker) { + const breakerToSwitchDom: HTMLElement | null = document.getElementById(locallySwitchedBreaker); + if (breakerToSwitchDom?.classList.value.includes('sld-closed')) { breakerToSwitchDom.classList.replace('sld-closed', 'sld-open'); - } else if (breakerToSwitchDom.classList.value.includes('sld-open')) { + } else if (breakerToSwitchDom?.classList.value.includes('sld-open')) { breakerToSwitchDom.classList.replace('sld-open', 'sld-closed'); } } @@ -486,13 +533,15 @@ function SingleLineDiagramContent(props) { diagramViewer.getWidth() === diagramViewerRef.current.getWidth() && diagramViewer.getHeight() === diagramViewerRef.current.getHeight() ) { - diagramViewer.setViewBox(diagramViewerRef.current.getViewBox()); + const viewBox = diagramViewerRef.current.getViewBox(); + if (viewBox) { + diagramViewer.setViewBox(viewBox); + } } diagramViewerRef.current = diagramViewer; } }, [ - props.svgUrl, props.svg, props.svgMetadata, currentNode, @@ -518,7 +567,7 @@ function SingleLineDiagramContent(props) { useLayoutEffect(() => { if (!props.loadingState) { setModificationInProgress(false); - setLocallySwitchedBreaker(null); + setLocallySwitchedBreaker(undefined); } }, [ props.loadingState, // the only one changing @@ -564,7 +613,7 @@ function SingleLineDiagramContent(props) { {equipmentToConfigDynamicSimulationEvent && ( handleCloseDynamicSimulationEventDialog()} @@ -575,15 +624,4 @@ function SingleLineDiagramContent(props) { ); } -SingleLineDiagramContent.propTypes = { - showInSpreadsheet: PropTypes.func, - studyUuid: PropTypes.string, - svgType: PropTypes.string, - svg: PropTypes.string, - svgMetadata: PropTypes.object, - loadingState: PropTypes.bool, - diagramSizeSetter: PropTypes.func, - diagramId: PropTypes.string, -}; - export default SingleLineDiagramContent; diff --git a/src/components/map-viewer.jsx b/src/components/map-viewer.jsx index eb8d3c1632..480bc093fc 100644 --- a/src/components/map-viewer.jsx +++ b/src/components/map-viewer.jsx @@ -25,7 +25,6 @@ import NetworkMapTab from './network/network-map-tab'; import { DiagramPane } from './diagrams/diagram-pane'; import { StudyView } from './study-pane'; import { darken } from '@mui/material/styles'; -import ComputingType from './computing-status/computing-type'; import { Global, css } from '@emotion/react'; import { EQUIPMENT_TYPES } from './utils/equipment-types'; @@ -162,7 +161,6 @@ const MapViewer = ({ const studyDisplayMode = useSelector((state) => state.studyDisplayMode); - const oneBusShortCircuitStatus = useSelector((state) => state.computingStatus[ComputingType.SHORT_CIRCUIT_ONE_BUS]); const previousStudyDisplayMode = useRef(undefined); const [nominalVoltages, setNominalVoltages] = useState(); @@ -357,7 +355,6 @@ const MapViewer = ({ view === StudyView.MAP && studyDisplayMode !== StudyDisplayMode.TREE } - oneBusShortCircuitStatus={oneBusShortCircuitStatus} /> void; - onOpenDynamicSimulationEventDialog: (equipmentId: string, equipmentType: string, dialogTitle: string) => void; + onOpenDynamicSimulationEventDialog: ( + equipmentId: string, + equipmentType: EQUIPMENT_TYPES, + dialogTitle: string + ) => void; position: [number, number]; onClose: () => void; setModificationInProgress: (progress: boolean) => void; @@ -107,7 +111,7 @@ export const BusMenu: FunctionComponent = ({ }, [busId, onClose, handleRunShortcircuitAnalysis]); const handleOpenDynamicSimulationEventDialog = useCallback( - (equipmentId: string, equipmentType: string, dialogTitle: string) => { + (equipmentId: string, equipmentType: EQUIPMENT_TYPES, dialogTitle: string) => { onClose(); onOpenDynamicSimulationEventDialog(equipmentId, equipmentType, dialogTitle); }, diff --git a/src/components/menus/dynamic-simulation/dynamic-simulation-event-menu-item.tsx b/src/components/menus/dynamic-simulation/dynamic-simulation-event-menu-item.tsx index ef56a0f32e..06ee0652db 100644 --- a/src/components/menus/dynamic-simulation/dynamic-simulation-event-menu-item.tsx +++ b/src/components/menus/dynamic-simulation/dynamic-simulation-event-menu-item.tsx @@ -13,6 +13,7 @@ import ListItemText from '@mui/material/ListItemText'; import Typography from '@mui/material/Typography'; import { useIntl } from 'react-intl'; import { CustomMenuItem } from '../../utils/custom-nested-menu'; +import { EQUIPMENT_TYPES } from 'components/utils/equipment-types'; const styles = { menuItem: { @@ -25,8 +26,12 @@ const styles = { interface DynamicSimulationEventMenuItemProps { equipmentId: string; - equipmentType: string; - onOpenDynamicSimulationEventDialog: (equipmentId: string, equipmentType: string, dialogTitle: string) => void; + equipmentType: EQUIPMENT_TYPES; + onOpenDynamicSimulationEventDialog: ( + equipmentId: string, + equipmentType: EQUIPMENT_TYPES, + dialogTitle: string + ) => void; disabled: boolean; } diff --git a/src/redux/reducer.test.ts b/src/redux/reducer.test.ts index db98135c32..c7cbf77983 100644 --- a/src/redux/reducer.test.ts +++ b/src/redux/reducer.test.ts @@ -22,6 +22,7 @@ import { } from './actions'; import { DiagramType, ViewState } from '../components/diagrams/diagram-common'; import { Reducer } from 'redux'; +import { UUID } from 'crypto'; const reducer = appReducer as Reducer, Actions>; @@ -57,7 +58,7 @@ test('reducer.SET_FULLSCREEN_DIAGRAM', () => { // From initial values const initialState = { fullScreenDiagram: null }; const expectedState = { - fullScreenDiagram: { id: '3', svgType: DiagramType.VOLTAGE_LEVEL }, + fullScreenDiagram: { id: '3' as UUID, svgType: DiagramType.VOLTAGE_LEVEL }, }; expect(reducer(initialState, setFullScreenDiagram('3', DiagramType.VOLTAGE_LEVEL))).toEqual(expectedState); @@ -65,12 +66,12 @@ test('reducer.SET_FULLSCREEN_DIAGRAM', () => { // Changing the fullscreen diagram const initialState2 = { fullScreenDiagram: { - id: '6', + id: '6' as UUID, svgType: DiagramType.NETWORK_AREA_DIAGRAM, }, }; const expectedState2 = { - fullScreenDiagram: { id: '12', svgType: DiagramType.SUBSTATION }, + fullScreenDiagram: { id: '12' as UUID, svgType: DiagramType.SUBSTATION }, }; expect(reducer(initialState2, setFullScreenDiagram('12', DiagramType.SUBSTATION))).toEqual(expectedState2); @@ -78,7 +79,7 @@ test('reducer.SET_FULLSCREEN_DIAGRAM', () => { // Removing the fullscreen const initialState3 = { fullScreenDiagram: { - id: '18', + id: '18' as UUID, svgType: DiagramType.NETWORK_AREA_DIAGRAM, }, }; @@ -93,7 +94,7 @@ test('reducer.OPEN_DIAGRAM.sld_specific', () => { const expectedState = { diagramStates: [ { - id: '65', + id: '65' as UUID, svgType: DiagramType.SUBSTATION, state: ViewState.OPENED, }, @@ -106,7 +107,7 @@ test('reducer.OPEN_DIAGRAM.sld_specific', () => { const initialState2 = { diagramStates: [ { - id: '174', + id: '174' as UUID, svgType: DiagramType.VOLTAGE_LEVEL, state: ViewState.OPENED, }, @@ -115,7 +116,7 @@ test('reducer.OPEN_DIAGRAM.sld_specific', () => { const expectedState2 = { diagramStates: [ { - id: '174', + id: '174' as UUID, svgType: DiagramType.VOLTAGE_LEVEL, state: ViewState.OPENED, needsToBlink: true, @@ -129,12 +130,12 @@ test('reducer.OPEN_DIAGRAM.sld_specific', () => { const initialState3 = { diagramStates: [ { - id: '34', + id: '34' as UUID, svgType: DiagramType.SUBSTATION, state: ViewState.MINIMIZED, }, { - id: '35', + id: '35' as UUID, svgType: DiagramType.VOLTAGE_LEVEL, state: ViewState.OPENED, }, @@ -143,25 +144,25 @@ test('reducer.OPEN_DIAGRAM.sld_specific', () => { const expectedState3 = { diagramStates: [ { - id: '35', + id: '35' as UUID, svgType: DiagramType.VOLTAGE_LEVEL, state: ViewState.MINIMIZED, }, { - id: '34', + id: '34' as UUID, svgType: DiagramType.SUBSTATION, state: ViewState.OPENED, }, ], }; - expect(reducer(initialState3, openDiagram('34', DiagramType.SUBSTATION))).toEqual(expectedState3); + expect(reducer(initialState3, openDiagram('34' as UUID, DiagramType.SUBSTATION))).toEqual(expectedState3); // Open a SLD that is already pinned const initialState4 = { diagramStates: [ { - id: '99', + id: '99' as UUID, svgType: DiagramType.VOLTAGE_LEVEL, state: ViewState.PINNED, }, @@ -170,7 +171,7 @@ test('reducer.OPEN_DIAGRAM.sld_specific', () => { const expectedState4 = { diagramStates: [ { - id: '99', + id: '99' as UUID, svgType: DiagramType.VOLTAGE_LEVEL, state: ViewState.PINNED, needsToBlink: true, @@ -178,13 +179,13 @@ test('reducer.OPEN_DIAGRAM.sld_specific', () => { ], }; - expect(reducer(initialState4, openDiagram('99', DiagramType.VOLTAGE_LEVEL))).toEqual(expectedState4); + expect(reducer(initialState4, openDiagram('99' as UUID, DiagramType.VOLTAGE_LEVEL))).toEqual(expectedState4); // Open a SLD when a NAD with the same ID is already opened const initialState5 = { diagramStates: [ { - id: '50', + id: '50' as UUID, svgType: DiagramType.NETWORK_AREA_DIAGRAM, state: ViewState.OPENED, }, @@ -193,12 +194,12 @@ test('reducer.OPEN_DIAGRAM.sld_specific', () => { const expectedState5 = { diagramStates: [ { - id: '50', + id: '50' as UUID, svgType: DiagramType.NETWORK_AREA_DIAGRAM, state: ViewState.OPENED, }, { - id: '50', + id: '50' as UUID, svgType: DiagramType.SUBSTATION, state: ViewState.OPENED, }, @@ -211,32 +212,32 @@ test('reducer.OPEN_DIAGRAM.sld_specific', () => { const initialState6 = { diagramStates: [ { - id: '101', + id: '101' as UUID, svgType: DiagramType.NETWORK_AREA_DIAGRAM, state: ViewState.OPENED, }, { - id: '102', + id: '102' as UUID, svgType: DiagramType.SUBSTATION, state: ViewState.PINNED, }, { - id: '103', + id: '103' as UUID, svgType: DiagramType.VOLTAGE_LEVEL, state: ViewState.MINIMIZED, }, { - id: '104', + id: '104' as UUID, svgType: DiagramType.VOLTAGE_LEVEL, state: ViewState.OPENED, }, // Should be minimized { - id: '105', + id: '105' as UUID, svgType: DiagramType.VOLTAGE_LEVEL, state: ViewState.PINNED, }, { - id: '106', + id: '106' as UUID, svgType: DiagramType.NETWORK_AREA_DIAGRAM, state: ViewState.OPENED, }, @@ -245,37 +246,37 @@ test('reducer.OPEN_DIAGRAM.sld_specific', () => { const expectedState6 = { diagramStates: [ { - id: '101', + id: '101' as UUID, svgType: DiagramType.NETWORK_AREA_DIAGRAM, state: ViewState.OPENED, }, { - id: '102', + id: '102' as UUID, svgType: DiagramType.SUBSTATION, state: ViewState.PINNED, }, { - id: '103', + id: '103' as UUID, svgType: DiagramType.VOLTAGE_LEVEL, state: ViewState.MINIMIZED, }, { - id: '104', + id: '104' as UUID, svgType: DiagramType.VOLTAGE_LEVEL, state: ViewState.MINIMIZED, }, { - id: '105', + id: '105' as UUID, svgType: DiagramType.VOLTAGE_LEVEL, state: ViewState.PINNED, }, { - id: '106', + id: '106' as UUID, svgType: DiagramType.NETWORK_AREA_DIAGRAM, state: ViewState.OPENED, }, { - id: '107', + id: '107' as UUID, svgType: DiagramType.SUBSTATION, state: ViewState.OPENED, }, // The new SLD is the only opened SLD @@ -288,12 +289,12 @@ test('reducer.OPEN_DIAGRAM.sld_specific', () => { const initialState7 = { diagramStates: [ { - id: '82', + id: '82' as UUID, svgType: DiagramType.SUBSTATION, state: ViewState.OPENED, }, { - id: '83', + id: '83' as UUID, svgType: DiagramType.SUBSTATION, state: ViewState.PINNED, }, @@ -303,17 +304,17 @@ test('reducer.OPEN_DIAGRAM.sld_specific', () => { const expectedState7 = { diagramStates: [ { - id: '82', + id: '82' as UUID, svgType: DiagramType.SUBSTATION, state: ViewState.MINIMIZED, }, { - id: '83', + id: '83' as UUID, svgType: DiagramType.SUBSTATION, state: ViewState.PINNED, }, { - id: '503', + id: '503' as UUID, svgType: DiagramType.VOLTAGE_LEVEL, state: ViewState.OPENED, }, @@ -330,7 +331,7 @@ test('reducer.OPEN_DIAGRAM.nad_specific', () => { const expectedState = { diagramStates: [ { - id: '37', + id: '37' as UUID, svgType: DiagramType.NETWORK_AREA_DIAGRAM, state: ViewState.OPENED, }, @@ -343,7 +344,7 @@ test('reducer.OPEN_DIAGRAM.nad_specific', () => { const initialState2 = { diagramStates: [ { - id: '18', + id: '18' as UUID, svgType: DiagramType.NETWORK_AREA_DIAGRAM, state: ViewState.OPENED, }, @@ -352,7 +353,7 @@ test('reducer.OPEN_DIAGRAM.nad_specific', () => { const expectedState2 = { diagramStates: [ { - id: '18', + id: '18' as UUID, svgType: DiagramType.NETWORK_AREA_DIAGRAM, state: ViewState.OPENED, }, @@ -365,7 +366,7 @@ test('reducer.OPEN_DIAGRAM.nad_specific', () => { const initialState3 = { diagramStates: [ { - id: '51', + id: '51' as UUID, svgType: DiagramType.NETWORK_AREA_DIAGRAM, state: ViewState.MINIMIZED, }, @@ -374,7 +375,7 @@ test('reducer.OPEN_DIAGRAM.nad_specific', () => { const expectedState3 = { diagramStates: [ { - id: '51', + id: '51' as UUID, svgType: DiagramType.NETWORK_AREA_DIAGRAM, state: ViewState.OPENED, }, @@ -387,7 +388,7 @@ test('reducer.OPEN_DIAGRAM.nad_specific', () => { const initialState4 = { diagramStates: [ { - id: '74', + id: '74' as UUID, svgType: DiagramType.NETWORK_AREA_DIAGRAM, state: ViewState.OPENED, }, @@ -396,12 +397,12 @@ test('reducer.OPEN_DIAGRAM.nad_specific', () => { const expectedState4 = { diagramStates: [ { - id: '74', + id: '74' as UUID, svgType: DiagramType.NETWORK_AREA_DIAGRAM, state: ViewState.OPENED, }, { - id: '22', + id: '22' as UUID, svgType: DiagramType.NETWORK_AREA_DIAGRAM, state: ViewState.OPENED, }, @@ -414,7 +415,7 @@ test('reducer.OPEN_DIAGRAM.nad_specific', () => { const initialState5 = { diagramStates: [ { - id: '33', + id: '33' as UUID, svgType: DiagramType.NETWORK_AREA_DIAGRAM, state: ViewState.MINIMIZED, }, @@ -423,72 +424,72 @@ test('reducer.OPEN_DIAGRAM.nad_specific', () => { const expectedState5 = { diagramStates: [ { - id: '33', + id: '33' as UUID, svgType: DiagramType.NETWORK_AREA_DIAGRAM, state: ViewState.OPENED, }, { - id: '44', + id: '44' as UUID, svgType: DiagramType.NETWORK_AREA_DIAGRAM, state: ViewState.OPENED, }, ], }; - expect(reducer(initialState5, openDiagram('44', DiagramType.NETWORK_AREA_DIAGRAM))).toEqual(expectedState5); + expect(reducer(initialState5, openDiagram('44' as UUID, DiagramType.NETWORK_AREA_DIAGRAM))).toEqual(expectedState5); // Open a NAD when there is no other NAD and an SLD is in fullscreen const initialState6 = { diagramStates: [ { - id: '38', + id: '38' as UUID, svgType: DiagramType.VOLTAGE_LEVEL, state: ViewState.PINNED, }, ], - fullScreenDiagram: { id: '38', svgType: DiagramType.VOLTAGE_LEVEL }, + fullScreenDiagram: { id: '38' as UUID, svgType: DiagramType.VOLTAGE_LEVEL }, }; const expectedState6 = { diagramStates: [ { - id: '38', + id: '38' as UUID, svgType: DiagramType.VOLTAGE_LEVEL, state: ViewState.PINNED, }, { - id: '28', + id: '28' as UUID, svgType: DiagramType.NETWORK_AREA_DIAGRAM, state: ViewState.OPENED, }, ], fullScreenDiagram: { - id: '28', + id: '28' as UUID, svgType: DiagramType.NETWORK_AREA_DIAGRAM, }, }; - expect(reducer(initialState6, openDiagram('28', DiagramType.NETWORK_AREA_DIAGRAM))).toEqual(expectedState6); + expect(reducer(initialState6, openDiagram('28' as UUID, DiagramType.NETWORK_AREA_DIAGRAM))).toEqual(expectedState6); // Open a NAD when there is another opened NAD and an SLD is in fullscreen const initialState7 = { diagramStates: [ { - id: '14', + id: '14' as UUID, svgType: DiagramType.VOLTAGE_LEVEL, state: ViewState.OPENED, }, { - id: '14', + id: '14' as UUID, svgType: DiagramType.NETWORK_AREA_DIAGRAM, state: ViewState.MINIMIZED, }, ], - fullScreenDiagram: { id: '14', svgType: DiagramType.VOLTAGE_LEVEL }, + fullScreenDiagram: { id: '14' as UUID, svgType: DiagramType.VOLTAGE_LEVEL }, }; const expectedState7 = { diagramStates: [ { - id: '14', + id: '14' as UUID, svgType: DiagramType.VOLTAGE_LEVEL, state: ViewState.OPENED, }, @@ -498,13 +499,13 @@ test('reducer.OPEN_DIAGRAM.nad_specific', () => { state: ViewState.OPENED, }, { - id: '39', + id: '39' as UUID, svgType: DiagramType.NETWORK_AREA_DIAGRAM, state: ViewState.OPENED, }, ], fullScreenDiagram: { - id: '14', + id: '14' as UUID, svgType: DiagramType.NETWORK_AREA_DIAGRAM, }, }; @@ -515,31 +516,31 @@ test('reducer.OPEN_DIAGRAM.nad_specific', () => { const initialState8 = { diagramStates: [ { - id: '85', + id: '85' as UUID, svgType: DiagramType.NETWORK_AREA_DIAGRAM, state: ViewState.PINNED, }, ], fullScreenDiagram: { - id: '85', + id: '85' as UUID, svgType: DiagramType.NETWORK_AREA_DIAGRAM, }, }; const expectedState8 = { diagramStates: [ { - id: '85', + id: '85' as UUID, svgType: DiagramType.NETWORK_AREA_DIAGRAM, state: ViewState.PINNED, }, { - id: '79', + id: '79' as UUID, svgType: DiagramType.NETWORK_AREA_DIAGRAM, state: ViewState.PINNED, }, ], fullScreenDiagram: { - id: '85', + id: '85' as UUID, svgType: DiagramType.NETWORK_AREA_DIAGRAM, }, }; @@ -558,7 +559,7 @@ test('reducer.MINIMIZE_DIAGRAM.sld_specific', () => { const initialState2 = { diagramStates: [ { - id: '12', + id: '12' as UUID, svgType: DiagramType.VOLTAGE_LEVEL, state: ViewState.OPENED, }, @@ -567,7 +568,7 @@ test('reducer.MINIMIZE_DIAGRAM.sld_specific', () => { const expectedState2 = { diagramStates: [ { - id: '12', + id: '12' as UUID, svgType: DiagramType.VOLTAGE_LEVEL, state: ViewState.OPENED, }, @@ -580,7 +581,7 @@ test('reducer.MINIMIZE_DIAGRAM.sld_specific', () => { const initialState3 = { diagramStates: [ { - id: '7', + id: '7' as UUID, svgType: DiagramType.SUBSTATION, state: ViewState.OPENED, }, @@ -589,7 +590,7 @@ test('reducer.MINIMIZE_DIAGRAM.sld_specific', () => { const expectedState3 = { diagramStates: [ { - id: '7', + id: '7' as UUID, svgType: DiagramType.SUBSTATION, state: ViewState.MINIMIZED, }, @@ -602,17 +603,17 @@ test('reducer.MINIMIZE_DIAGRAM.sld_specific', () => { const initialState4 = { diagramStates: [ { - id: '63', + id: '63' as UUID, svgType: DiagramType.SUBSTATION, state: ViewState.OPENED, }, { - id: '47', + id: '47' as UUID, svgType: DiagramType.SUBSTATION, state: ViewState.PINNED, }, { - id: '25', + id: '25' as UUID, svgType: DiagramType.NETWORK_AREA_DIAGRAM, state: ViewState.OPENED, }, @@ -621,17 +622,17 @@ test('reducer.MINIMIZE_DIAGRAM.sld_specific', () => { const expectedState4 = { diagramStates: [ { - id: '63', + id: '63' as UUID, svgType: DiagramType.SUBSTATION, state: ViewState.OPENED, }, { - id: '47', + id: '47' as UUID, svgType: DiagramType.SUBSTATION, state: ViewState.MINIMIZED, }, { - id: '25', + id: '25' as UUID, svgType: DiagramType.NETWORK_AREA_DIAGRAM, state: ViewState.OPENED, }, @@ -644,17 +645,17 @@ test('reducer.MINIMIZE_DIAGRAM.sld_specific', () => { const initialState5 = { diagramStates: [ { - id: '1', + id: '1' as UUID, svgType: DiagramType.SUBSTATION, state: ViewState.PINNED, }, { - id: '1', + id: '1' as UUID, svgType: DiagramType.VOLTAGE_LEVEL, state: ViewState.MINIMIZED, }, { - id: '22', + id: '22' as UUID, svgType: DiagramType.VOLTAGE_LEVEL, state: ViewState.PINNED, }, @@ -663,17 +664,17 @@ test('reducer.MINIMIZE_DIAGRAM.sld_specific', () => { const expectedState5 = { diagramStates: [ { - id: '1', + id: '1' as UUID, svgType: DiagramType.SUBSTATION, state: ViewState.PINNED, }, { - id: '1', + id: '1' as UUID, svgType: DiagramType.VOLTAGE_LEVEL, state: ViewState.MINIMIZED, }, { - id: '22', + id: '22' as UUID, svgType: DiagramType.VOLTAGE_LEVEL, state: ViewState.PINNED, }, @@ -688,32 +689,32 @@ test('reducer.MINIMIZE_DIAGRAM.nad_specific', () => { const initialState = { diagramStates: [ { - id: '10', + id: '10' as UUID, svgType: DiagramType.VOLTAGE_LEVEL, state: ViewState.PINNED, }, { - id: '200', + id: '200' as UUID, svgType: DiagramType.SUBSTATION, state: ViewState.PINNED, }, { - id: '10', + id: '10' as UUID, svgType: DiagramType.NETWORK_AREA_DIAGRAM, state: ViewState.OPENED, }, { - id: '200', + id: '200' as UUID, svgType: DiagramType.NETWORK_AREA_DIAGRAM, state: ViewState.OPENED, }, { - id: '3', + id: '3' as UUID, svgType: DiagramType.VOLTAGE_LEVEL, state: ViewState.PINNED, }, { - id: '4', + id: '4' as UUID, svgType: DiagramType.NETWORK_AREA_DIAGRAM, state: ViewState.OPENED, }, @@ -722,32 +723,32 @@ test('reducer.MINIMIZE_DIAGRAM.nad_specific', () => { const expectedState = { diagramStates: [ { - id: '10', + id: '10' as UUID, svgType: DiagramType.VOLTAGE_LEVEL, state: ViewState.PINNED, }, { - id: '200', + id: '200' as UUID, svgType: DiagramType.SUBSTATION, state: ViewState.PINNED, }, { - id: '10', + id: '10' as UUID, svgType: DiagramType.NETWORK_AREA_DIAGRAM, state: ViewState.MINIMIZED, }, { - id: '200', + id: '200' as UUID, svgType: DiagramType.NETWORK_AREA_DIAGRAM, state: ViewState.MINIMIZED, }, { - id: '3', + id: '3' as UUID, svgType: DiagramType.VOLTAGE_LEVEL, state: ViewState.PINNED, }, { - id: '4', + id: '4' as UUID, svgType: DiagramType.NETWORK_AREA_DIAGRAM, state: ViewState.MINIMIZED, }, @@ -762,12 +763,12 @@ test('reducer.TOGGLE_PIN_DIAGRAM.sld_specific', () => { const initialState = { diagramStates: [ { - id: '1', + id: '1' as UUID, svgType: DiagramType.VOLTAGE_LEVEL, state: ViewState.OPENED, }, { - id: '2', + id: '2' as UUID, svgType: DiagramType.NETWORK_AREA_DIAGRAM, state: ViewState.PINNED, }, @@ -776,12 +777,12 @@ test('reducer.TOGGLE_PIN_DIAGRAM.sld_specific', () => { const expectedState = { diagramStates: [ { - id: '1', + id: '1' as UUID, svgType: DiagramType.VOLTAGE_LEVEL, state: ViewState.OPENED, }, { - id: '2', + id: '2' as UUID, svgType: DiagramType.NETWORK_AREA_DIAGRAM, state: ViewState.PINNED, }, @@ -794,12 +795,12 @@ test('reducer.TOGGLE_PIN_DIAGRAM.sld_specific', () => { const initialState2 = { diagramStates: [ { - id: '1', + id: '1' as UUID, svgType: DiagramType.VOLTAGE_LEVEL, state: ViewState.MINIMIZED, }, { - id: '2', + id: '2' as UUID, svgType: DiagramType.SUBSTATION, state: ViewState.OPENED, }, @@ -808,12 +809,12 @@ test('reducer.TOGGLE_PIN_DIAGRAM.sld_specific', () => { const expectedState2 = { diagramStates: [ { - id: '1', + id: '1' as UUID, svgType: DiagramType.VOLTAGE_LEVEL, state: ViewState.MINIMIZED, }, { - id: '2', + id: '2' as UUID, svgType: DiagramType.SUBSTATION, state: ViewState.PINNED, }, @@ -826,17 +827,17 @@ test('reducer.TOGGLE_PIN_DIAGRAM.sld_specific', () => { const initialState3 = { diagramStates: [ { - id: '31', + id: '31' as UUID, svgType: DiagramType.SUBSTATION, state: ViewState.MINIMIZED, }, { - id: '32', + id: '32' as UUID, svgType: DiagramType.SUBSTATION, state: ViewState.PINNED, }, { - id: '33', + id: '33' as UUID, svgType: DiagramType.VOLTAGE_LEVEL, state: ViewState.PINNED, }, @@ -845,17 +846,17 @@ test('reducer.TOGGLE_PIN_DIAGRAM.sld_specific', () => { const expectedState3 = { diagramStates: [ { - id: '31', + id: '31' as UUID, svgType: DiagramType.SUBSTATION, state: ViewState.MINIMIZED, }, { - id: '32', + id: '32' as UUID, svgType: DiagramType.SUBSTATION, state: ViewState.OPENED, }, { - id: '33', + id: '33' as UUID, svgType: DiagramType.VOLTAGE_LEVEL, state: ViewState.PINNED, }, @@ -868,27 +869,27 @@ test('reducer.TOGGLE_PIN_DIAGRAM.sld_specific', () => { const initialState4 = { diagramStates: [ { - id: '10', + id: '10' as UUID, svgType: DiagramType.SUBSTATION, state: ViewState.MINIMIZED, }, { - id: '20', + id: '20' as UUID, svgType: DiagramType.SUBSTATION, state: ViewState.PINNED, }, { - id: '30', + id: '30' as UUID, svgType: DiagramType.SUBSTATION, state: ViewState.OPENED, }, { - id: '40', + id: '40' as UUID, svgType: DiagramType.VOLTAGE_LEVEL, state: ViewState.PINNED, }, { - id: '50', + id: '50' as UUID, svgType: DiagramType.NETWORK_AREA_DIAGRAM, state: ViewState.OPENED, }, @@ -897,27 +898,27 @@ test('reducer.TOGGLE_PIN_DIAGRAM.sld_specific', () => { const expectedState4 = { diagramStates: [ { - id: '10', + id: '10' as UUID, svgType: DiagramType.SUBSTATION, state: ViewState.MINIMIZED, }, { - id: '20', + id: '20' as UUID, svgType: DiagramType.SUBSTATION, state: ViewState.PINNED, }, { - id: '30', + id: '30' as UUID, svgType: DiagramType.SUBSTATION, state: ViewState.OPENED, }, { - id: '40', + id: '40' as UUID, svgType: DiagramType.VOLTAGE_LEVEL, state: ViewState.MINIMIZED, }, { - id: '50', + id: '50' as UUID, svgType: DiagramType.NETWORK_AREA_DIAGRAM, state: ViewState.OPENED, }, @@ -932,12 +933,12 @@ test('reducer.TOGGLE_PIN_DIAGRAM.nad_specific', () => { const initialState = { diagramStates: [ { - id: '1', + id: '1' as UUID, svgType: DiagramType.VOLTAGE_LEVEL, state: ViewState.OPENED, }, { - id: '2', + id: '2' as UUID, svgType: DiagramType.NETWORK_AREA_DIAGRAM, state: ViewState.PINNED, }, @@ -946,12 +947,12 @@ test('reducer.TOGGLE_PIN_DIAGRAM.nad_specific', () => { const expectedState = { diagramStates: [ { - id: '1', + id: '1' as UUID, svgType: DiagramType.VOLTAGE_LEVEL, state: ViewState.OPENED, }, { - id: '2', + id: '2' as UUID, svgType: DiagramType.NETWORK_AREA_DIAGRAM, state: ViewState.PINNED, }, @@ -964,17 +965,17 @@ test('reducer.TOGGLE_PIN_DIAGRAM.nad_specific', () => { const initialState2 = { diagramStates: [ { - id: '1', + id: '1' as UUID, svgType: DiagramType.SUBSTATION, state: ViewState.OPENED, }, { - id: '2', + id: '2' as UUID, svgType: DiagramType.NETWORK_AREA_DIAGRAM, state: ViewState.PINNED, }, { - id: '3', + id: '3' as UUID, svgType: DiagramType.NETWORK_AREA_DIAGRAM, state: ViewState.PINNED, }, @@ -983,17 +984,17 @@ test('reducer.TOGGLE_PIN_DIAGRAM.nad_specific', () => { const expectedState2 = { diagramStates: [ { - id: '1', + id: '1' as UUID, svgType: DiagramType.SUBSTATION, state: ViewState.OPENED, }, { - id: '2', + id: '2' as UUID, svgType: DiagramType.NETWORK_AREA_DIAGRAM, state: ViewState.OPENED, }, { - id: '3', + id: '3' as UUID, svgType: DiagramType.NETWORK_AREA_DIAGRAM, state: ViewState.OPENED, }, @@ -1006,17 +1007,17 @@ test('reducer.TOGGLE_PIN_DIAGRAM.nad_specific', () => { const initialState3 = { diagramStates: [ { - id: '3', + id: '3' as UUID, svgType: DiagramType.NETWORK_AREA_DIAGRAM, state: ViewState.OPENED, }, { - id: '4', + id: '4' as UUID, svgType: DiagramType.VOLTAGE_LEVEL, state: ViewState.MINIMIZED, }, { - id: '5', + id: '5' as UUID, svgType: DiagramType.NETWORK_AREA_DIAGRAM, state: ViewState.OPENED, }, @@ -1025,12 +1026,12 @@ test('reducer.TOGGLE_PIN_DIAGRAM.nad_specific', () => { const expectedState3 = { diagramStates: [ { - id: '3', + id: '3' as UUID, svgType: DiagramType.NETWORK_AREA_DIAGRAM, state: ViewState.PINNED, }, { - id: '4', + id: '4' as UUID, svgType: DiagramType.VOLTAGE_LEVEL, state: ViewState.MINIMIZED, }, @@ -1050,7 +1051,7 @@ test('reducer.CLOSE_DIAGRAM', () => { const initialState = { diagramStates: [ { - id: '6', + id: '6' as UUID, svgType: DiagramType.VOLTAGE_LEVEL, state: ViewState.PINNED, }, @@ -1059,7 +1060,7 @@ test('reducer.CLOSE_DIAGRAM', () => { const expectedState = { diagramStates: [ { - id: '6', + id: '6' as UUID, svgType: DiagramType.VOLTAGE_LEVEL, state: ViewState.PINNED, }, @@ -1072,12 +1073,12 @@ test('reducer.CLOSE_DIAGRAM', () => { const initialState2 = { diagramStates: [ { - id: '3', + id: '3' as UUID, svgType: DiagramType.VOLTAGE_LEVEL, state: ViewState.PINNED, }, { - id: '54', + id: '54' as UUID, svgType: DiagramType.SUBSTATION, state: ViewState.OPENED, }, @@ -1086,7 +1087,7 @@ test('reducer.CLOSE_DIAGRAM', () => { const expectedState2 = { diagramStates: [ { - id: '3', + id: '3' as UUID, svgType: DiagramType.VOLTAGE_LEVEL, state: ViewState.PINNED, }, @@ -1099,22 +1100,22 @@ test('reducer.CLOSE_DIAGRAM', () => { const initialState3 = { diagramStates: [ { - id: '32', + id: '32' as UUID, svgType: DiagramType.NETWORK_AREA_DIAGRAM, state: ViewState.PINNED, }, { - id: '64', + id: '64' as UUID, svgType: DiagramType.NETWORK_AREA_DIAGRAM, state: ViewState.PINNED, }, { - id: '64', + id: '64' as UUID, svgType: DiagramType.SUBSTATION, state: ViewState.OPENED, }, { - id: '82', + id: '82' as UUID, svgType: DiagramType.NETWORK_AREA_DIAGRAM, state: ViewState.PINNED, }, @@ -1123,7 +1124,7 @@ test('reducer.CLOSE_DIAGRAM', () => { const expectedState3 = { diagramStates: [ { - id: '64', + id: '64' as UUID, svgType: DiagramType.SUBSTATION, state: ViewState.OPENED, }, @@ -1138,32 +1139,32 @@ test('reducer.CLOSE_DIAGRAMS', () => { const initialState = { diagramStates: [ { - id: '10', + id: '10' as UUID, svgType: DiagramType.SUBSTATION, state: ViewState.MINIMIZED, }, { - id: '20', + id: '20' as UUID, svgType: DiagramType.SUBSTATION, state: ViewState.PINNED, }, { - id: '30', + id: '30' as UUID, svgType: DiagramType.SUBSTATION, state: ViewState.OPENED, }, { - id: '5', + id: '5' as UUID, svgType: DiagramType.NETWORK_AREA_DIAGRAM, state: ViewState.OPENED, }, { - id: '10', + id: '10' as UUID, svgType: DiagramType.VOLTAGE_LEVEL, state: ViewState.PINNED, }, { - id: '10', + id: '10' as UUID, svgType: DiagramType.NETWORK_AREA_DIAGRAM, state: ViewState.OPENED, }, @@ -1172,12 +1173,12 @@ test('reducer.CLOSE_DIAGRAMS', () => { const expectedState = { diagramStates: [ { - id: '20', + id: '20' as UUID, svgType: DiagramType.SUBSTATION, state: ViewState.PINNED, }, { - id: '5', + id: '5' as UUID, svgType: DiagramType.NETWORK_AREA_DIAGRAM, state: ViewState.OPENED, }, @@ -1191,18 +1192,18 @@ test('reducer.STOP_DIAGRAM_BLINK', () => { const initialState = { diagramStates: [ { - id: '102', + id: '102' as UUID, svgType: DiagramType.SUBSTATION, state: ViewState.MINIMIZED, }, { - id: '202', + id: '202' as UUID, svgType: DiagramType.SUBSTATION, state: ViewState.PINNED, needsToBlink: true, }, { - id: '302', + id: '302' as UUID, svgType: DiagramType.SUBSTATION, state: ViewState.OPENED, needsToBlink: true, @@ -1212,17 +1213,17 @@ test('reducer.STOP_DIAGRAM_BLINK', () => { const expectedState = { diagramStates: [ { - id: '102', + id: '102' as UUID, svgType: DiagramType.SUBSTATION, state: ViewState.MINIMIZED, }, { - id: '202', + id: '202' as UUID, svgType: DiagramType.SUBSTATION, state: ViewState.PINNED, }, { - id: '302', + id: '302' as UUID, svgType: DiagramType.SUBSTATION, state: ViewState.OPENED, }, diff --git a/src/redux/reducer.ts b/src/redux/reducer.ts index 98cdc15dd7..34c93248e4 100644 --- a/src/redux/reducer.ts +++ b/src/redux/reducer.ts @@ -388,7 +388,7 @@ export type SpreadsheetEquipmentType = Exclude; export type DiagramState = { - id: string; + id: UUID; svgType: DiagramType; state: ViewState; needsToBlink?: boolean; @@ -1192,7 +1192,7 @@ export const reducer = createReducer(initialState, (builder) => { if (firstNadIndex < 0) { // If there is no NAD, then we add the new one. diagramStates.push({ - id: action.id, + id: action.id as UUID, svgType: DiagramType.NETWORK_AREA_DIAGRAM, state: ViewState.OPENED, }); @@ -1216,7 +1216,7 @@ export const reducer = createReducer(initialState, (builder) => { // If the NAD to open is not already in the diagramStates, we add it. if (diagramToOpenIndex < 0) { diagramStates.push({ - id: action.id, + id: action.id as UUID, svgType: DiagramType.NETWORK_AREA_DIAGRAM, state: diagramStates[firstNadIndex].state, }); @@ -1273,7 +1273,7 @@ export const reducer = createReducer(initialState, (builder) => { }); // And we add the new one. diagramStates.push({ - id: action.id, + id: action.id as UUID, svgType: action.svgType, state: ViewState.OPENED, }); @@ -1300,7 +1300,7 @@ export const reducer = createReducer(initialState, (builder) => { state.diagramStates = diagramStatesWithoutNad.concat( uniqueIds.map((id) => ({ - id: id, + id: id as UUID, svgType: DiagramType.NETWORK_AREA_DIAGRAM, state: ViewState.OPENED, }))