mirror of
https://github.com/kubeshark/kubeshark.git
synced 2025-06-29 17:49:40 +00:00
with loading HOC (#1181)
* withLoading * optional props * LoadingWrapper * pr comments * changes Co-authored-by: Leon <>
This commit is contained in:
parent
5f73c2d50a
commit
f3a6b3a9d4
@ -12,8 +12,8 @@ import TrafficViewerApiAtom from "../../recoil/TrafficViewerApi/atom";
|
|||||||
import queryAtom from "../../recoil/query/atom";
|
import queryAtom from "../../recoil/query/atom";
|
||||||
import useWindowDimensions, { useRequestTextByWidth } from "../../hooks/WindowDimensionsHook";
|
import useWindowDimensions, { useRequestTextByWidth } from "../../hooks/WindowDimensionsHook";
|
||||||
import { TOAST_CONTAINER_ID } from "../../configs/Consts";
|
import { TOAST_CONTAINER_ID } from "../../configs/Consts";
|
||||||
import spinner from "assets/spinner.svg";
|
|
||||||
import entryDataAtom from "../../recoil/entryData";
|
import entryDataAtom from "../../recoil/entryData";
|
||||||
|
import { LoadingWrapper } from "../UI/withLoading/withLoading";
|
||||||
|
|
||||||
const useStyles = makeStyles(() => ({
|
const useStyles = makeStyles(() => ({
|
||||||
entryTitle: {
|
entryTitle: {
|
||||||
@ -135,19 +135,11 @@ export const EntryDetailed = () => {
|
|||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
}, [focusedEntryId]);
|
}, [focusedEntryId]);
|
||||||
|
|
||||||
return <React.Fragment>
|
return <LoadingWrapper isLoading={isLoading} loaderMargin={50} loaderHeight={60}>
|
||||||
{isLoading && <div style={{textAlign: "center", width: "100%", marginTop: 50}}><img alt="spinner" src={spinner} style={{height: 60}}/></div>}
|
{entryData && <React.Fragment>
|
||||||
{!isLoading && entryData && <EntryTitle
|
<EntryTitle protocol={entryData.protocol} data={entryData.data} elapsedTime={entryData.data.elapsedTime} />
|
||||||
protocol={entryData.protocol}
|
<EntrySummary entry={entryData.base} namespace={entryData.data.namespace} />
|
||||||
data={entryData.data}
|
<EntryViewer representation={entryData.representation} color={entryData.protocol.backgroundColor} />
|
||||||
elapsedTime={entryData.data.elapsedTime}
|
</React.Fragment>}
|
||||||
/>}
|
</LoadingWrapper>
|
||||||
{!isLoading && entryData && <EntrySummary entry={entryData.base} namespace={entryData.data.namespace} />}
|
|
||||||
<React.Fragment>
|
|
||||||
{!isLoading && entryData && <EntryViewer
|
|
||||||
representation={entryData.representation}
|
|
||||||
color={entryData.protocol.backgroundColor}
|
|
||||||
/>}
|
|
||||||
</React.Fragment>
|
|
||||||
</React.Fragment>
|
|
||||||
};
|
};
|
||||||
|
@ -8,6 +8,7 @@ import CustomModal from "./CustomModal/CustomModal";
|
|||||||
import { InformationIcon, Link } from "./InformationIcon/InformationIcon";
|
import { InformationIcon, Link } from "./InformationIcon/InformationIcon";
|
||||||
import SelectList from "./SelectList/SelectList";
|
import SelectList from "./SelectList/SelectList";
|
||||||
import NoDataMessage from "./NoDataMessage/NoDataMessage";
|
import NoDataMessage from "./NoDataMessage/NoDataMessage";
|
||||||
|
import withLoading from "./withLoading/withLoading";
|
||||||
|
|
||||||
export { LoadingOverlay, Select, Tabs, Tooltip, Checkbox, CustomModal, InformationIcon, SelectList, NoDataMessage, Link };
|
export { LoadingOverlay, Select, Tabs, Tooltip, Checkbox, CustomModal, InformationIcon, SelectList, NoDataMessage, withLoading, Link };
|
||||||
export { StatusBar }
|
export { StatusBar }
|
||||||
|
6
ui-common/src/components/UI/withLoading/spinner.svg
Normal file
6
ui-common/src/components/UI/withLoading/spinner.svg
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="margin: auto; background: none; display: block; shape-rendering: auto;" width="200px" height="200px" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid">
|
||||||
|
<circle cx="50" cy="50" fill="none" stroke="#1d3f72" stroke-width="10" r="35" stroke-dasharray="164.93361431346415 56.97787143782138" transform="rotate(275.903 50 50)">
|
||||||
|
<animateTransform attributeName="transform" type="rotate" repeatCount="indefinite" dur="1s" values="0 50 50;360 50 50" keyTimes="0;1"></animateTransform>
|
||||||
|
</circle>
|
||||||
|
<!-- [ldio] generated by https://loading.io/ --></svg>
|
After Width: | Height: | Size: 673 B |
33
ui-common/src/components/UI/withLoading/withLoading.tsx
Normal file
33
ui-common/src/components/UI/withLoading/withLoading.tsx
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import React from "react";
|
||||||
|
import spinner from 'spinner.svg';
|
||||||
|
|
||||||
|
export interface WithLoadingProps {
|
||||||
|
isLoading: boolean
|
||||||
|
loaderMargin?: number,
|
||||||
|
loaderHeight?: number
|
||||||
|
}
|
||||||
|
|
||||||
|
const Loader = ({ loaderMargin = 20, loaderHeight = 35 }: Omit<WithLoadingProps, "isLoading">) => {
|
||||||
|
return <div style={{ textAlign: "center", margin: loaderMargin }}>
|
||||||
|
<img alt="spinner" src={spinner} style={{ height: loaderHeight }} />
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
const withLoading = <P extends object>(
|
||||||
|
Component: React.ComponentType<P>
|
||||||
|
): React.FC<P & WithLoadingProps> => ({
|
||||||
|
isLoading,
|
||||||
|
loaderMargin,
|
||||||
|
loaderHeight,
|
||||||
|
...props
|
||||||
|
}: WithLoadingProps) => isLoading ?
|
||||||
|
<Loader loaderMargin={loaderMargin} loaderHeight={loaderHeight} /> :
|
||||||
|
<Component {...props as P} />;
|
||||||
|
|
||||||
|
export const LoadingWrapper: React.FC<WithLoadingProps> = ({ loaderMargin, loaderHeight, isLoading, children }) => {
|
||||||
|
return isLoading ?
|
||||||
|
<Loader loaderMargin={loaderMargin} loaderHeight={loaderHeight} /> :
|
||||||
|
<React.Fragment>{children}</React.Fragment>
|
||||||
|
}
|
||||||
|
|
||||||
|
export default withLoading
|
@ -12,7 +12,6 @@ import { toast } from "react-toastify";
|
|||||||
import { TOAST_CONTAINER_ID } from "../../../configs/Consts";
|
import { TOAST_CONTAINER_ID } from "../../../configs/Consts";
|
||||||
import styles from './ReplayRequestModal.module.sass'
|
import styles from './ReplayRequestModal.module.sass'
|
||||||
import closeIcon from "assets/close.svg"
|
import closeIcon from "assets/close.svg"
|
||||||
import spinnerImg from "assets/spinner.svg"
|
|
||||||
import refreshImg from "assets/refresh.svg"
|
import refreshImg from "assets/refresh.svg"
|
||||||
import { formatRequestWithOutError } from "../../EntryDetailed/EntrySections/EntrySections";
|
import { formatRequestWithOutError } from "../../EntryDetailed/EntrySections/EntrySections";
|
||||||
import entryDataAtom from "../../../recoil/entryData";
|
import entryDataAtom from "../../../recoil/entryData";
|
||||||
@ -20,6 +19,7 @@ import { AutoRepresentation, TabsEnum } from "../../EntryDetailed/EntryViewer/Au
|
|||||||
import useDebounce from "../../../hooks/useDebounce"
|
import useDebounce from "../../../hooks/useDebounce"
|
||||||
import replayRequestModalOpenAtom from "../../../recoil/replayRequestModalOpen";
|
import replayRequestModalOpenAtom from "../../../recoil/replayRequestModalOpen";
|
||||||
import { Utils } from "../../../helpers/Utils";
|
import { Utils } from "../../../helpers/Utils";
|
||||||
|
import { LoadingWrapper } from "../../UI/withLoading/withLoading";
|
||||||
|
|
||||||
const modalStyle = {
|
const modalStyle = {
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
@ -233,15 +233,16 @@ const ReplayRequestModal: React.FC<ReplayRequestModalProps> = ({ isOpen, onClose
|
|||||||
</div>
|
</div>
|
||||||
</AccordionDetails>
|
</AccordionDetails>
|
||||||
</Accordion>
|
</Accordion>
|
||||||
{isLoading && <img alt="spinner" src={spinnerImg} style={{ height: 50 }} />}
|
<LoadingWrapper isLoading={isLoading} loaderMargin={10} loaderHeight={50}>
|
||||||
{response && !isLoading && (<Accordion TransitionProps={{ unmountOnExit: true }} expanded={responseExpanded} onChange={() => setResponseExpanded(!responseExpanded)}>
|
{response && (<Accordion TransitionProps={{ unmountOnExit: true }} expanded={responseExpanded} onChange={() => setResponseExpanded(!responseExpanded)}>
|
||||||
<AccordionSummary expandIcon={<ExpandMoreIcon />} aria-controls="response-content">
|
<AccordionSummary expandIcon={<ExpandMoreIcon />} aria-controls="response-content">
|
||||||
<span className={styles.sectionHeader}>RESPONSE</span>
|
<span className={styles.sectionHeader}>RESPONSE</span>
|
||||||
</AccordionSummary>
|
</AccordionSummary>
|
||||||
<AccordionDetails>
|
<AccordionDetails>
|
||||||
<AutoRepresentation representation={response} color={entryData.protocol.backgroundColor} openedTab={TabsEnum.Response} />
|
<AutoRepresentation representation={response} color={entryData.protocol.backgroundColor} openedTab={TabsEnum.Response} />
|
||||||
</AccordionDetails>
|
</AccordionDetails>
|
||||||
</Accordion>)}
|
</Accordion>)}
|
||||||
|
</LoadingWrapper>
|
||||||
</div>
|
</div>
|
||||||
</Box>
|
</Box>
|
||||||
</Fade>
|
</Fade>
|
||||||
|
@ -96,8 +96,3 @@ $modalMargin-from-edge : 35px
|
|||||||
|
|
||||||
.servicesFilterList
|
.servicesFilterList
|
||||||
height: calc(100% - 30px - 52px)
|
height: calc(100% - 30px - 52px)
|
||||||
|
|
||||||
.spinnerContainer
|
|
||||||
display: flex
|
|
||||||
justify-content: center
|
|
||||||
margin-bottom: 10px
|
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import React, { useState, useEffect, useCallback, useMemo } from "react";
|
import React, { useState, useEffect, useCallback, useMemo } from "react";
|
||||||
import { Box, Fade, Modal, Backdrop, Button } from "@mui/material";
|
import { Box, Fade, Modal, Backdrop, Button } from "@mui/material";
|
||||||
import { toast } from "react-toastify";
|
import { toast } from "react-toastify";
|
||||||
import spinnerImg from 'assets/spinner.svg';
|
|
||||||
import Graph from "react-graph-vis";
|
import Graph from "react-graph-vis";
|
||||||
import debounce from 'lodash/debounce';
|
import debounce from 'lodash/debounce';
|
||||||
import ServiceMapOptions from './ServiceMapOptions'
|
import ServiceMapOptions from './ServiceMapOptions'
|
||||||
@ -16,6 +15,7 @@ import { GraphData, ServiceMapGraph } from "./ServiceMapModalTypes"
|
|||||||
import { Utils } from "../../../helpers/Utils";
|
import { Utils } from "../../../helpers/Utils";
|
||||||
import { TOAST_CONTAINER_ID } from "../../../configs/Consts";
|
import { TOAST_CONTAINER_ID } from "../../../configs/Consts";
|
||||||
import Resizeable from "../../UI/Resizeable/Resizeable"
|
import Resizeable from "../../UI/Resizeable/Resizeable"
|
||||||
|
import { LoadingWrapper } from "../../UI/withLoading/withLoading";
|
||||||
|
|
||||||
const modalStyle = {
|
const modalStyle = {
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
@ -197,14 +197,14 @@ export const ServiceMapModal: React.FC<ServiceMapModalProps> = ({ isOpen, onClos
|
|||||||
<Fade in={isOpen}>
|
<Fade in={isOpen}>
|
||||||
<Box sx={modalStyle}>
|
<Box sx={modalStyle}>
|
||||||
<div className={styles.closeIcon}>
|
<div className={styles.closeIcon}>
|
||||||
<img src={closeIcon} alt="close" onClick={() => onClose()} style={{ cursor: "pointer", userSelect: "none" }}/>
|
<img src={closeIcon} alt="close" onClick={() => onClose()} style={{ cursor: "pointer", userSelect: "none" }} />
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.headerContainer}>
|
<div className={styles.headerContainer}>
|
||||||
<div className={styles.headerSection}>
|
<div className={styles.headerSection}>
|
||||||
<span className={styles.title}>Services</span>
|
<span className={styles.title}>Services</span>
|
||||||
<Button size="medium"
|
<Button size="medium"
|
||||||
variant="contained"
|
variant="contained"
|
||||||
startIcon={<img src={isFilterClicked ? filterIconClicked : filterIcon} className="custom" alt="refresh" style={{ height: "26px", width: "26px" }}/>}
|
startIcon={<img src={isFilterClicked ? filterIconClicked : filterIcon} className="custom" alt="refresh" style={{ height: "26px", width: "26px" }} />}
|
||||||
className={commonClasses.outlinedButton + " " + commonClasses.imagedButton + ` ${isFilterClicked ? commonClasses.clickedButton : ""}`}
|
className={commonClasses.outlinedButton + " " + commonClasses.imagedButton + ` ${isFilterClicked ? commonClasses.clickedButton : ""}`}
|
||||||
onClick={() => setIsFilterClicked(prevState => !prevState)}
|
onClick={() => setIsFilterClicked(prevState => !prevState)}
|
||||||
style={{ textTransform: 'unset' }}>
|
style={{ textTransform: 'unset' }}>
|
||||||
@ -243,16 +243,14 @@ export const ServiceMapModal: React.FC<ServiceMapModalProps> = ({ isOpen, onClos
|
|||||||
<div className={styles.graphSection}>
|
<div className={styles.graphSection}>
|
||||||
<div style={{ display: "flex", justifyContent: "space-between" }}>
|
<div style={{ display: "flex", justifyContent: "space-between" }}>
|
||||||
</div>
|
</div>
|
||||||
{isLoading && <div className={styles.spinnerContainer}>
|
<LoadingWrapper isLoading={isLoading} loaderHeight={50} loaderMargin={20}>
|
||||||
<img alt="spinner" src={spinnerImg} style={{ height: 50 }} />
|
<div style={{ height: "100%", width: "100%" }}>
|
||||||
</div>}
|
<Graph
|
||||||
{!isLoading && <div style={{ height: "100%", width: "100%" }}>
|
graph={graphData}
|
||||||
<Graph
|
options={graphOptions}
|
||||||
graph={graphData}
|
/>
|
||||||
options={graphOptions}
|
</div>
|
||||||
/>
|
</LoadingWrapper>
|
||||||
</div>
|
|
||||||
}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Box>
|
</Box>
|
||||||
|
@ -4,9 +4,9 @@ import styles from "./TrafficStatsModal.module.sass";
|
|||||||
import closeIcon from "assets/close.svg";
|
import closeIcon from "assets/close.svg";
|
||||||
import { TrafficPieChart } from "./TrafficPieChart/TrafficPieChart";
|
import { TrafficPieChart } from "./TrafficPieChart/TrafficPieChart";
|
||||||
import { TimelineBarChart } from "./TimelineBarChart/TimelineBarChart";
|
import { TimelineBarChart } from "./TimelineBarChart/TimelineBarChart";
|
||||||
import spinnerImg from "assets/spinner.svg";
|
|
||||||
import refreshIcon from "assets/refresh.svg";
|
import refreshIcon from "assets/refresh.svg";
|
||||||
import { useCommonStyles } from "../../../helpers/commonStyle";
|
import { useCommonStyles } from "../../../helpers/commonStyle";
|
||||||
|
import { LoadingWrapper } from "../../UI/withLoading/withLoading";
|
||||||
|
|
||||||
const modalStyle = {
|
const modalStyle = {
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
@ -114,13 +114,12 @@ export const TrafficStatsModal: React.FC<TrafficStatsModalProps> = ({ isOpen, on
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
{isLoading ? <div style={{ textAlign: "center", marginTop: 20 }}>
|
<LoadingWrapper isLoading={isLoading} loaderMargin={20} loaderHeight={50}>
|
||||||
<img alt="spinner" src={spinnerImg} style={{ height: 50 }} />
|
|
||||||
</div> :
|
|
||||||
<div>
|
<div>
|
||||||
<TrafficPieChart pieChartMode={statsMode} data={pieStatsData} selectedProtocol={selectedProtocol}/>
|
<TrafficPieChart pieChartMode={statsMode} data={pieStatsData} selectedProtocol={selectedProtocol} />
|
||||||
<TimelineBarChart timeLineBarChartMode={statsMode} data={timelineStatsData} selectedProtocol={selectedProtocol}/>
|
<TimelineBarChart timeLineBarChartMode={statsMode} data={timelineStatsData} selectedProtocol={selectedProtocol} />
|
||||||
</div>}
|
</div>
|
||||||
|
</LoadingWrapper>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Box>
|
</Box>
|
||||||
|
Loading…
Reference in New Issue
Block a user