diff --git a/ui-common/src/components/TrafficViewer/EntriesList.tsx b/ui-common/src/components/TrafficViewer/EntriesList.tsx index e34f8e195..ceaed9015 100644 --- a/ui-common/src/components/TrafficViewer/EntriesList.tsx +++ b/ui-common/src/components/TrafficViewer/EntriesList.tsx @@ -5,107 +5,120 @@ import Moment from 'moment'; import {EntryItem} from "./EntryListItem/EntryListItem"; import down from "assets/downImg.svg"; import spinner from 'assets/spinner.svg'; -import {RecoilState, useRecoilState, useRecoilValue} from "recoil"; +import {RecoilState, useRecoilState, useRecoilValue, useSetRecoilState} from "recoil"; import entriesAtom from "../../recoil/entries"; import queryAtom from "../../recoil/query"; import TrafficViewerApiAtom from "../../recoil/TrafficViewerApi"; import TrafficViewerApi from "./TrafficViewerApi"; import focusedEntryIdAtom from "../../recoil/focusedEntryId"; +import {toast} from "react-toastify"; +import {TOAST_CONTAINER_ID} from "../../configs/Consts"; +import tappingStatusAtom from "../../recoil/tappingStatus"; +import leftOffTopAtom from "../../recoil/leftOffTop"; interface EntriesListProps { - listEntryREF: any; - onSnapBrokenEvent: () => void; - isSnappedToBottom: boolean; - setIsSnappedToBottom: any; - queriedCurrent: number; - setQueriedCurrent: any; - startTime: number; - noMoreDataTop: boolean; - setNoMoreDataTop: (flag: boolean) => void; - leftOffTop: number; - setLeftOffTop: (leftOffTop: number) => void; - openWebSocket: (query: string, resetEntries: boolean) => void; - leftOffBottom: number; - truncatedTimestamp: number; - setTruncatedTimestamp: any; - scrollableRef: any; - ws: any; + listEntryREF: any; + onSnapBrokenEvent: () => void; + isSnappedToBottom: boolean; + setIsSnappedToBottom: any; + noMoreDataTop: boolean; + setNoMoreDataTop: (flag: boolean) => void; + openWebSocket: (query: string, resetEntries: boolean) => void; + scrollableRef: any; + ws: any; } -export const EntriesList: React.FC = ({listEntryREF, onSnapBrokenEvent, isSnappedToBottom, setIsSnappedToBottom, queriedCurrent, setQueriedCurrent, startTime, noMoreDataTop, setNoMoreDataTop, leftOffTop, setLeftOffTop, openWebSocket, leftOffBottom, truncatedTimestamp, setTruncatedTimestamp, scrollableRef, ws}) => { +export const EntriesList: React.FC = ({ + listEntryREF, + onSnapBrokenEvent, + isSnappedToBottom, + setIsSnappedToBottom, + noMoreDataTop, + setNoMoreDataTop, + openWebSocket, + scrollableRef, + ws + }) => { - const [entries, setEntries] = useRecoilState(entriesAtom); - const query = useRecoilValue(queryAtom); - const isWsConnectionClosed = ws?.current?.readyState !== WebSocket.OPEN; - const [focusedEntryId, setFocusedEntryId] = useRecoilState(focusedEntryIdAtom); + const [entries, setEntries] = useRecoilState(entriesAtom); + const query = useRecoilValue(queryAtom); + const isWsConnectionClosed = ws?.current?.readyState !== WebSocket.OPEN; + const [focusedEntryId, setFocusedEntryId] = useRecoilState(focusedEntryIdAtom); + const [leftOffTop, setLeftOffTop] = useRecoilState(leftOffTopAtom); + const setTappingStatus = useSetRecoilState(tappingStatusAtom); - const trafficViewerApi = useRecoilValue(TrafficViewerApiAtom as RecoilState) + const trafficViewerApi = useRecoilValue(TrafficViewerApiAtom as RecoilState) - const [loadMoreTop, setLoadMoreTop] = useState(false); - const [isLoadingTop, setIsLoadingTop] = useState(false); - const [queriedTotal, setQueriedTotal] = useState(0); + const [loadMoreTop, setLoadMoreTop] = useState(false); + const [isLoadingTop, setIsLoadingTop] = useState(false); + const [queriedTotal, setQueriedTotal] = useState(0); + const [startTime, setStartTime] = useState(0); + const [truncatedTimestamp, setTruncatedTimestamp] = useState(0); - useEffect(() => { - const list = document.getElementById('list').firstElementChild; - list.addEventListener('scroll', (e) => { - const el: any = e.target; - if(el.scrollTop === 0) { - setLoadMoreTop(true); - } else { - setNoMoreDataTop(false); - setLoadMoreTop(false); - } - }); - }, [setLoadMoreTop, setNoMoreDataTop]); + const leftOffBottom = entries.length > 0 ? entries[entries.length - 1].id : -1; - const memoizedEntries = useMemo(() => { - return entries; - },[entries]); - - const getOldEntries = useCallback(async () => { + useEffect(() => { + const list = document.getElementById('list').firstElementChild; + list.addEventListener('scroll', (e) => { + const el: any = e.target; + if (el.scrollTop === 0) { + setLoadMoreTop(true); + } else { + setNoMoreDataTop(false); setLoadMoreTop(false); - if (leftOffTop === null || leftOffTop <= 0) { - return; - } - setIsLoadingTop(true); - const data = await trafficViewerApi.fetchEntries(leftOffTop, -1, query, 100, 3000); - if (!data || data.data === null || data.meta === null) { - setNoMoreDataTop(true); - setIsLoadingTop(false); - return; - } - setLeftOffTop(data.meta.leftOff); + } + }); + }, [setLoadMoreTop, setNoMoreDataTop]); - let scrollTo: boolean; - if (data.meta.leftOff === 0) { - setNoMoreDataTop(true); - scrollTo = false; - } else { - scrollTo = true; - } - setIsLoadingTop(false); + const memoizedEntries = useMemo(() => { + return entries; + }, [entries]); - const newEntries = [...data.data.reverse(), ...entries]; - setEntries(newEntries); + const getOldEntries = useCallback(async () => { + setLoadMoreTop(false); + if (leftOffTop === null || leftOffTop <= 0) { + return; + } + setIsLoadingTop(true); + const data = await trafficViewerApi.fetchEntries(leftOffTop, -1, query, 100, 3000); + if (!data || data.data === null || data.meta === null) { + setNoMoreDataTop(true); + setIsLoadingTop(false); + return; + } + setLeftOffTop(data.meta.leftOff); - setQueriedCurrent(queriedCurrent + data.meta.current); - setQueriedTotal(data.meta.total); - setTruncatedTimestamp(data.meta.truncatedTimestamp); + let scrollTo: boolean; + if (data.meta.leftOff === 0) { + setNoMoreDataTop(true); + scrollTo = false; + } else { + scrollTo = true; + } + setIsLoadingTop(false); - if (scrollTo) { - scrollableRef.current.scrollToIndex(data.data.length - 1); - } - },[setLoadMoreTop, setIsLoadingTop, entries, setEntries, query, setNoMoreDataTop, leftOffTop, setLeftOffTop, queriedCurrent, setQueriedCurrent, setQueriedTotal, setTruncatedTimestamp, scrollableRef]); + const newEntries = [...data.data.reverse(), ...entries]; + setEntries(newEntries); + + setQueriedTotal(data.meta.total); + setTruncatedTimestamp(data.meta.truncatedTimestamp); + + if (scrollTo) { + scrollableRef.current.scrollToIndex(data.data.length - 1); + } + }, [setLoadMoreTop, setIsLoadingTop, entries, setEntries, query, setNoMoreDataTop, leftOffTop, setLeftOffTop, setQueriedTotal, setTruncatedTimestamp, scrollableRef]); + + useEffect(() => { + if (!isWsConnectionClosed || !loadMoreTop || noMoreDataTop) return; + getOldEntries(); + }, [loadMoreTop, noMoreDataTop, getOldEntries, isWsConnectionClosed]); + + const scrollbarVisible = scrollableRef.current?.childWrapperRef.current.clientHeight > scrollableRef.current?.wrapperRef.current.clientHeight; - useEffect(() => { - if(!isWsConnectionClosed || !loadMoreTop || noMoreDataTop) return; - getOldEntries(); - }, [loadMoreTop, noMoreDataTop, getOldEntries, isWsConnectionClosed]); - const scrollbarVisible = scrollableRef.current?.childWrapperRef.current.clientHeight > scrollableRef.current?.wrapperRef.current.clientHeight; if (ws.current) { - ws.current.addEventListener("message", (e) => { + ws.current.onmessage = (e) => { if (!e?.data) return; const message = JSON.parse(e.data); switch (message.messageType) { @@ -120,60 +133,86 @@ export const EntriesList: React.FC = ({listEntryREF, onSnapBro } setEntries(newEntries); break; + case "status": + setTappingStatus(message.tappingStatus); + break; + case "toast": + toast[message.data.type](message.data.text, { + theme: "colored", + autoClose: message.data.autoClose, + pauseOnHover: true, + progress: undefined, + containerId: TOAST_CONTAINER_ID + }); + break; case "queryMetadata": + setTruncatedTimestamp(message.data.truncatedTimestamp); setQueriedTotal(message.data.total); + if (leftOffTop === null) { + setLeftOffTop(message.data.leftOff - 1); + } + break; + case "startTime": + setStartTime(message.data); break; }; - }); + } } - return -
-
- {isLoadingTop &&
- spinner -
} - {noMoreDataTop &&
No more data available
} - - {false /* It's because the first child is ignored by ScrollableFeedVirtualized */} - {memoizedEntries.map(entry => )} - - - -
+ return +
+
+ {isLoadingTop &&
+ spinner +
} + {noMoreDataTop &&
No more data available
} + + {false /* It's because the first child is ignored by ScrollableFeedVirtualized */} + {memoizedEntries.map(entry => )} + + + +
-
-
Displaying {entries?.length} results out of {queriedTotal} total
- {startTime !== 0 &&
Started listening at {Moment(truncatedTimestamp ? truncatedTimestamp : startTime).utc().format('MM/DD/YYYY, h:mm:ss.SSS A')}
} -
-
-
; +
+
Displaying {entries?.length} results out of {queriedTotal} total +
+ {startTime !== 0 &&
Started listening at {Moment(truncatedTimestamp ? truncatedTimestamp : startTime).utc().format('MM/DD/YYYY, h:mm:ss.SSS A')} +
} +
+
+
; }; diff --git a/ui-common/src/components/TrafficViewer/EntryDetailed.tsx b/ui-common/src/components/TrafficViewer/EntryDetailed.tsx index 895168b5a..c54dc6b24 100644 --- a/ui-common/src/components/TrafficViewer/EntryDetailed.tsx +++ b/ui-common/src/components/TrafficViewer/EntryDetailed.tsx @@ -5,14 +5,14 @@ import { makeStyles } from "@material-ui/core"; import Protocol from "../UI/Protocol" import Queryable from "../UI/Queryable"; import { toast } from "react-toastify"; -import { RecoilState, useRecoilState, useRecoilValue } from "recoil"; +import { RecoilState, useRecoilValue } from "recoil"; import focusedEntryIdAtom from "../../recoil/focusedEntryId"; -import trafficViewerApi from "../../recoil/TrafficViewerApi"; import TrafficViewerApi from "./TrafficViewerApi"; import TrafficViewerApiAtom from "../../recoil/TrafficViewerApi/atom"; import queryAtom from "../../recoil/query/atom"; import useWindowDimensions, { useRequestTextByWidth } from "../../hooks/WindowDimensionsHook"; import { TOAST_CONTAINER_ID } from "../../configs/Consts"; +import spinner from "assets/spinner.svg"; const useStyles = makeStyles(() => ({ entryTitle: { @@ -105,12 +105,13 @@ export const EntryDetailed = () => { const focusedEntryId = useRecoilValue(focusedEntryIdAtom); const trafficViewerApi = useRecoilValue(TrafficViewerApiAtom as RecoilState) const query = useRecoilValue(queryAtom); - + const [isLoading, setIsLoading] = useState(false); const [entryData, setEntryData] = useState(null); useEffect(() => { if (!focusedEntryId) return; setEntryData(null); + setIsLoading(true); (async () => { try { const entryData = await trafficViewerApi.getEntry(focusedEntryId, query); @@ -125,20 +126,23 @@ export const EntryDetailed = () => { }); } console.error(error); + } finally { + setIsLoading(false); } })(); // eslint-disable-next-line }, [focusedEntryId]); return - {entryData && spinner} + {!isLoading && entryData && } - {entryData && } + {!isLoading && entryData && } - {entryData && void; + reopenConnection: any; } -export const Filters: React.FC = ({backgroundColor, openWebSocket}) => { +export const Filters: React.FC = ({backgroundColor, reopenConnection}) => { return
; }; interface QueryFormProps { backgroundColor: string - openWebSocket: (query: string, resetEntries: boolean) => void; + reopenConnection: any; } export const modalStyle = { @@ -47,11 +47,10 @@ export const modalStyle = { color: '#000', }; -export const QueryForm: React.FC = ({backgroundColor, openWebSocket}) => { +export const QueryForm: React.FC = ({backgroundColor, reopenConnection}) => { const formRef = useRef(null); const [query, setQuery] = useRecoilState(queryAtom); - const trafficViewerApi = useRecoilValue(trafficViewerApiAtom) const [openModal, setOpenModal] = useState(false); @@ -63,12 +62,7 @@ export const QueryForm: React.FC = ({backgroundColor, openWebSoc } const handleSubmit = (e) => { - trafficViewerApi.webSocket.close() - if (query) { - openWebSocket(`(${query}) and leftOff(-1)`, true); - } else { - openWebSocket(`leftOff(-1)`, true); - } + reopenConnection(); e.preventDefault(); } diff --git a/ui-common/src/components/TrafficViewer/TrafficViewer.tsx b/ui-common/src/components/TrafficViewer/TrafficViewer.tsx index deb4c1d89..d9391ff6c 100644 --- a/ui-common/src/components/TrafficViewer/TrafficViewer.tsx +++ b/ui-common/src/components/TrafficViewer/TrafficViewer.tsx @@ -1,25 +1,26 @@ -import React, { useEffect, useMemo, useRef, useState } from "react"; -import { Filters } from "./Filters"; -import { EntriesList } from "./EntriesList"; -import { makeStyles } from "@material-ui/core"; +import React, {useEffect, useMemo, useRef, useState} from "react"; +import {Filters} from "./Filters"; +import {EntriesList} from "./EntriesList"; +import {makeStyles} from "@material-ui/core"; import TrafficViewerStyles from "./TrafficViewer.module.sass"; import styles from '../style/EntriesList.module.sass'; -import { EntryDetailed } from "./EntryDetailed"; +import {EntryDetailed} from "./EntryDetailed"; import playIcon from 'assets/run.svg'; import pauseIcon from 'assets/pause.svg'; import variables from '../../variables.module.scss'; -import { toast, ToastContainer } from 'react-toastify'; +import {ToastContainer} from 'react-toastify'; import debounce from 'lodash/debounce'; -import { RecoilRoot, RecoilState, useRecoilState, useRecoilValue, useSetRecoilState } from "recoil"; +import {RecoilRoot, RecoilState, useRecoilState, useRecoilValue, useSetRecoilState} from "recoil"; import entriesAtom from "../../recoil/entries"; import focusedEntryIdAtom from "../../recoil/focusedEntryId"; import queryAtom from "../../recoil/query"; -import { TLSWarning } from "../TLSWarning/TLSWarning"; +import {TLSWarning} from "../TLSWarning/TLSWarning"; import trafficViewerApiAtom from "../../recoil/TrafficViewerApi" import TrafficViewerApi from "./TrafficViewerApi"; -import { StatusBar } from "../UI/StatusBar"; +import {StatusBar} from "../UI/StatusBar"; import tappingStatusAtom from "../../recoil/tappingStatus/atom"; -import { TOAST_CONTAINER_ID } from "../../configs/Consts"; +import {TOAST_CONTAINER_ID} from "../../configs/Consts"; +import leftOffTopAtom from "../../recoil/leftOffTop"; const useLayoutStyles = makeStyles(() => ({ details: { @@ -52,14 +53,16 @@ interface TrafficViewerProps { isDemoBannerView: boolean } -export const TrafficViewer: React.FC = ({ setAnalyzeStatus, trafficViewerApiProp, - actionButtons, isShowStatusBar, webSocketUrl, - isCloseWebSocket, isDemoBannerView }) => { +export const TrafficViewer: React.FC = ({ + setAnalyzeStatus, trafficViewerApiProp, + actionButtons, isShowStatusBar, webSocketUrl, + isCloseWebSocket, isDemoBannerView + }) => { const classes = useLayoutStyles(); const setEntries = useSetRecoilState(entriesAtom); - const [focusedEntryId, setFocusedEntryId] = useRecoilState(focusedEntryIdAtom); + const setFocusedEntryId = useSetRecoilState(focusedEntryIdAtom); const query = useRecoilValue(queryAtom); const setTrafficViewerApiState = useSetRecoilState(trafficViewerApiAtom as RecoilState) const [tappingStatus, setTappingStatus] = useRecoilState(tappingStatusAtom); @@ -69,12 +72,7 @@ export const TrafficViewer: React.FC = ({ setAnalyzeStatus, const [queryBackgroundColor, setQueryBackgroundColor] = useState("#f5f5f5"); - const [queriedCurrent, setQueriedCurrent] = useState(0); - const [leftOffBottom, setLeftOffBottom] = useState(0); - const [leftOffTop, setLeftOffTop] = useState(null); - const [truncatedTimestamp, setTruncatedTimestamp] = useState(0); - - const [startTime, setStartTime] = useState(0); + const setLeftOffTop = useSetRecoilState(leftOffTopAtom); const scrollableRef = useRef(null); const [showTLSWarning, setShowTLSWarning] = useState(false); @@ -124,7 +122,7 @@ export const TrafficViewer: React.FC = ({ setAnalyzeStatus, } const closeWebSocket = () => { - if(ws?.current?.readyState === WebSocket.OPEN) { + if (ws?.current?.readyState === WebSocket.OPEN) { ws.current.close(); return true; } @@ -135,7 +133,6 @@ export const TrafficViewer: React.FC = ({ setAnalyzeStatus, if (resetEntries) { setFocusedEntryId(null); setEntries([]); - setQueriedCurrent(0); setLeftOffTop(null); setNoMoreDataTop(false); } @@ -155,67 +152,26 @@ export const TrafficViewer: React.FC = ({ setAnalyzeStatus, if (ws?.current?.readyState === WebSocket.OPEN) { ws.current.close(); } - if (query) { - openWebSocket(`(${query}) and leftOff(${leftOffBottom})`, false); - } else { - openWebSocket(`leftOff(${leftOffBottom})`, false); - } } - } catch (e) { } + } catch (e) { + } } const sendQueryWhenWsOpen = (query) => { setTimeout(() => { if (ws?.current?.readyState === WebSocket.OPEN) { - ws.current.send(JSON.stringify({ "query": query, "enableFullEntries": false })); + ws.current.send(JSON.stringify({"query": query, "enableFullEntries": false})); } else { sendQueryWhenWsOpen(query); } }, 500) } - if (ws.current) { - ws.current.addEventListener("message", (e) => { - if (!e?.data) return; - const message = JSON.parse(e.data); - switch (message.messageType) { - case "status": - setTappingStatus(message.tappingStatus); - break; - case "analyzeStatus": - setAnalyzeStatus(message.analyzeStatus); - break; - case "outboundLink": - onTLSDetected(message.Data.DstIP); - break; - case "toast": - toast[message.data.type](message.data.text, { - theme: "colored", - autoClose: message.data.autoClose, - pauseOnHover: true, - progress: undefined, - containerId: TOAST_CONTAINER_ID - }); - break; - case "queryMetadata": - setQueriedCurrent(queriedCurrent + message.data.current); - setLeftOffBottom(message.data.leftOff); - setTruncatedTimestamp(message.data.truncatedTimestamp); - if (leftOffTop === null) { - setLeftOffTop(message.data.leftOff - 1); - } - break; - case "startTime": - setStartTime(message.data); - break; - } - }) - } useEffect(() => { - setTrafficViewerApiState({ ...trafficViewerApiProp, webSocket: { close: closeWebSocket } }); + setTrafficViewerApiState({...trafficViewerApiProp, webSocket: {close: closeWebSocket}}); (async () => { - try{ + try { const tapStatusResponse = await trafficViewerApiProp.tapStatus(); setTappingStatus(tapStatusResponse); if (setAnalyzeStatus) { @@ -226,11 +182,10 @@ export const TrafficViewer: React.FC = ({ setAnalyzeStatus, console.error(error); } })() - // eslint-disable-next-line }, []); const toggleConnection = () => { - if(!closeWebSocket()) { + if (!closeWebSocket()) { openEmptyWebSocket(); scrollableRef.current.jumpToBottom(); setIsSnappedToBottom(true); @@ -240,6 +195,8 @@ export const TrafficViewer: React.FC = ({ setAnalyzeStatus, const reopenConnection = async () => { closeWebSocket() openEmptyWebSocket(); + scrollableRef.current.jumpToBottom(); + setIsSnappedToBottom(true); } useEffect(() => { @@ -248,24 +205,17 @@ export const TrafficViewer: React.FC = ({ setAnalyzeStatus, }; }, []); - const onTLSDetected = (destAddress: string) => { - addressesWithTLS.add(destAddress); - setAddressesWithTLS(new Set(addressesWithTLS)); - - if (!userDismissedTLSWarning) { - setShowTLSWarning(true); - } - }; - const getConnectionIndicator = () => { switch (wsReadyState) { case WebSocket.OPEN: - return
-
+ return
+
default: - return
-
+ return
+
} } @@ -288,13 +238,16 @@ export const TrafficViewer: React.FC = ({ setAnalyzeStatus, return (
- {tappingStatus && isShowStatusBar && } + {tappingStatus && isShowStatusBar && }
- pause - play + pause + play
{getConnectionTitle()} {getConnectionIndicator()} @@ -306,8 +259,7 @@ export const TrafficViewer: React.FC = ({ setAnalyzeStatus,
= ({ setAnalyzeStatus, onSnapBrokenEvent={onSnapBrokenEvent} isSnappedToBottom={isSnappedToBottom} setIsSnappedToBottom={setIsSnappedToBottom} - queriedCurrent={queriedCurrent} - setQueriedCurrent={setQueriedCurrent} - startTime={startTime} noMoreDataTop={noMoreDataTop} setNoMoreDataTop={setNoMoreDataTop} - leftOffTop={leftOffTop} - setLeftOffTop={setLeftOffTop} openWebSocket={openWebSocket} - leftOffBottom={leftOffBottom} - truncatedTimestamp={truncatedTimestamp} - setTruncatedTimestamp={setTruncatedTimestamp} scrollableRef={scrollableRef} ws={ws} />
- {focusedEntryId && } +
} + setShowTLSWarning={setShowTLSWarning} + addressesWithTLS={addressesWithTLS} + setAddressesWithTLS={setAddressesWithTLS} + userDismissedTLSWarning={userDismissedTLSWarning} + setUserDismissedTLSWarning={setUserDismissedTLSWarning}/>
); }; const MemoiedTrafficViewer = React.memo(TrafficViewer) -const TrafficViewerContainer: React.FC = ({ setAnalyzeStatus, trafficViewerApiProp, - actionButtons, isShowStatusBar = true, - webSocketUrl, isCloseWebSocket, isDemoBannerView }) => { +const TrafficViewerContainer: React.FC = ({ + setAnalyzeStatus, trafficViewerApiProp, + actionButtons, isShowStatusBar = true, + webSocketUrl, isCloseWebSocket, isDemoBannerView + }) => { return + isCloseWebSocket={isCloseWebSocket} trafficViewerApiProp={trafficViewerApiProp} + setAnalyzeStatus={setAnalyzeStatus} isDemoBannerView={isDemoBannerView}/> + position="bottom-right" + autoClose={5000} + hideProgressBar={false} + newestOnTop={false} + closeOnClick + rtl={false} + pauseOnFocusLoss + draggable + pauseOnHover/> } diff --git a/ui-common/src/components/UI/StatusBar.tsx b/ui-common/src/components/UI/StatusBar.tsx index 5b13b9a99..a12da99fe 100644 --- a/ui-common/src/components/UI/StatusBar.tsx +++ b/ui-common/src/components/UI/StatusBar.tsx @@ -14,11 +14,10 @@ interface StatusBarProps { isDemoBannerView: boolean; } -export const StatusBar = ({isDemoBannerView}) => { +export const StatusBar: React.FC = ({isDemoBannerView}) => { const tappingStatus = useRecoilValue(tappingStatusAtom); const [expandedBar, setExpandedBar] = useState(false); const {uniqueNamespaces, amountOfPods, amountOfTappedPods, amountOfUntappedPods} = useRecoilValue(tappingStatusDetails); - return
setExpandedBar(true)} onMouseLeave={() => setExpandedBar(false)} data-cy="expandedStatusBar">
{tappingStatus.some(pod => !pod.isTapped) && warning} @@ -39,7 +38,7 @@ export const StatusBar = ({isDemoBannerView}) => { {tappingStatus.map(pod => {pod.name} {pod.namespace} - status + {pod.isTapped ? status : status} )} diff --git a/ui-common/src/recoil/leftOffTop/atom.ts b/ui-common/src/recoil/leftOffTop/atom.ts new file mode 100644 index 000000000..9b9336691 --- /dev/null +++ b/ui-common/src/recoil/leftOffTop/atom.ts @@ -0,0 +1,8 @@ +import { atom } from "recoil" + +const leftOffTopAtom = atom({ + key: "leftOffTopAtom", + default: null +}) + +export default leftOffTopAtom; diff --git a/ui-common/src/recoil/leftOffTop/index.ts b/ui-common/src/recoil/leftOffTop/index.ts new file mode 100644 index 000000000..5d094a22c --- /dev/null +++ b/ui-common/src/recoil/leftOffTop/index.ts @@ -0,0 +1,2 @@ +import atom from "./atom"; +export default atom;