[Dashboard] Make loading screen not block out the entire page.
diff --git a/dashboard/client/src/components/Loading.tsx b/dashboard/client/src/components/Loading.tsx
index 6c1cb1e8f0eaa..edca6bb2063e2 100644
--- a/dashboard/client/src/components/Loading.tsx
+++ b/dashboard/client/src/components/Loading.tsx
@@ -1,10 +1,7 @@
-import { Backdrop, CircularProgress } from "@material-ui/core";
+import { CircularProgress } from "@material-ui/core";
 import React from "react";
 
-const Loading = ({ loading }: { loading: boolean }) => (
-  <Backdrop open={loading} style={{ zIndex: 100 }}>
-    <CircularProgress color="primary" />
-  </Backdrop>
-);
+const Loading = ({ loading }: { loading: boolean }) =>
+  loading ? <CircularProgress color="primary" /> : null;
 
 export default Loading;
diff --git a/dashboard/client/src/pages/actor/ActorDetail.tsx b/dashboard/client/src/pages/actor/ActorDetail.tsx
index 80783d6b1baa1..b0517766fe3c0 100644
--- a/dashboard/client/src/pages/actor/ActorDetail.tsx
+++ b/dashboard/client/src/pages/actor/ActorDetail.tsx
@@ -37,12 +37,12 @@ const useStyle = makeStyles((theme) => ({
 const ActorDetailPage = () => {
   const classes = useStyle();
   const { ipLogMap } = useContext(GlobalContext);
-  const { params, actorDetail, msg } = useActorDetail();
+  const { params, actorDetail, msg, isLoading } = useActorDetail();
 
   if (!actorDetail) {
     return (
       <div className={classes.root}>
-        <Loading loading={msg.startsWith("Loading")} />
+        <Loading loading={isLoading} />
         <TitleCard title={`JOB - ${params.id}`}>
           <StatusChip type="job" status="LOADING" />
           <br />
diff --git a/dashboard/client/src/pages/actor/hook/useActorDetail.ts b/dashboard/client/src/pages/actor/hook/useActorDetail.ts
index 271f903e076c1..86aad3aaa9594 100644
--- a/dashboard/client/src/pages/actor/hook/useActorDetail.ts
+++ b/dashboard/client/src/pages/actor/hook/useActorDetail.ts
@@ -10,7 +10,7 @@ export const useActorDetail = () => {
   const [msg, setMsg] = useState("Loading the actor infos...");
   const { namespaceMap } = useContext(GlobalContext);
 
-  const { data: actorDetail } = useSWR(
+  const { data: actorDetail, isLoading } = useSWR(
     ["useActorDetail", params.id],
     async ([_, actorId]) => {
       const actor_resp = await getActor(actorId);
@@ -35,6 +35,7 @@ export const useActorDetail = () => {
     params,
     actorDetail,
     msg,
+    isLoading,
     namespaceMap,
   };
 };
diff --git a/dashboard/client/src/pages/job/JobDetail.tsx b/dashboard/client/src/pages/job/JobDetail.tsx
index 888ae5eeb48db..e3721403dbc7e 100644
--- a/dashboard/client/src/pages/job/JobDetail.tsx
+++ b/dashboard/client/src/pages/job/JobDetail.tsx
@@ -31,7 +31,7 @@ const useStyle = makeStyles((theme) => ({
 
 export const JobDetailChartsPage = () => {
   const classes = useStyle();
-  const { job, msg, params } = useJobDetail();
+  const { job, msg, isLoading, params } = useJobDetail();
   const jobId = params.id;
 
   const [taskListFilter, setTaskListFilter] = useState<string>();
@@ -99,7 +99,7 @@ export const JobDetailChartsPage = () => {
   if (!job) {
     return (
       <div className={classes.root}>
-        <Loading loading={msg.startsWith("Loading")} />
+        <Loading loading={isLoading} />
         <TitleCard title={`JOB - ${params.id}`}>
           <StatusChip type="job" status="LOADING" />
           <br />
diff --git a/dashboard/client/src/pages/job/JobDetailInfoPage.tsx b/dashboard/client/src/pages/job/JobDetailInfoPage.tsx
index 24bb85b23e17c..b8345e8d44e49 100644
--- a/dashboard/client/src/pages/job/JobDetailInfoPage.tsx
+++ b/dashboard/client/src/pages/job/JobDetailInfoPage.tsx
@@ -20,7 +20,7 @@ export const JobDetailInfoPage = () => {
   // TODO(aguo): Add more content to this page!
 
   const classes = useStyle();
-  const { job, msg, params } = useJobDetail();
+  const { job, msg, isLoading, params } = useJobDetail();
 
   if (!job) {
     return (
@@ -32,7 +32,7 @@ export const JobDetailInfoPage = () => {
             path: undefined,
           }}
         />
-        <Loading loading={msg.startsWith("Loading")} />
+        <Loading loading={isLoading} />
         <TitleCard title={`JOB - ${params.id}`}>
           <StatusChip type="job" status="LOADING" />
           <br />
diff --git a/dashboard/client/src/pages/job/hook/useJobDetail.ts b/dashboard/client/src/pages/job/hook/useJobDetail.ts
index 7dda767a297e2..d473eb3cba314 100644
--- a/dashboard/client/src/pages/job/hook/useJobDetail.ts
+++ b/dashboard/client/src/pages/job/hook/useJobDetail.ts
@@ -10,7 +10,7 @@ export const useJobDetail = () => {
   const [msg, setMsg] = useState("Loading the job detail");
   const [refreshing, setRefresh] = useState(true);
   const { ipLogMap } = useContext(GlobalContext);
-  const { data: job } = useSWR(
+  const { data: job, isLoading } = useSWR(
     "useJobDetail",
     async () => {
       try {
@@ -26,6 +26,7 @@ export const useJobDetail = () => {
 
   return {
     job,
+    isLoading,
     msg,
     params,
     ipLogMap,
diff --git a/dashboard/client/src/pages/job/hook/useJobList.ts b/dashboard/client/src/pages/job/hook/useJobList.ts
index ba4beedb6b6c8..8ed6079be5a49 100644
--- a/dashboard/client/src/pages/job/hook/useJobList.ts
+++ b/dashboard/client/src/pages/job/hook/useJobList.ts
@@ -30,7 +30,7 @@ export const useJobList = () => {
   };
   refreshRef.current = isRefreshing;
 
-  const { data } = useSWR(
+  const { data, isLoading } = useSWR(
     "useJobList",
     async () => {
       const rsp = await getJobList();
@@ -52,6 +52,7 @@ export const useJobList = () => {
       filter.every((f) => node[f.key] && (node[f.key] ?? "").includes(f.val)),
     ),
     msg,
+    isLoading,
     isRefreshing,
     onSwitchChange,
     changeFilter,
diff --git a/dashboard/client/src/pages/job/index.tsx b/dashboard/client/src/pages/job/index.tsx
index 4fe82836ab34a..1ff5eb6346f6e 100644
--- a/dashboard/client/src/pages/job/index.tsx
+++ b/dashboard/client/src/pages/job/index.tsx
@@ -68,6 +68,7 @@ const JobList = () => {
   const classes = useStyles();
   const {
     msg,
+    isLoading,
     isRefreshing,
     onSwitchChange,
     jobList,
@@ -78,7 +79,7 @@ const JobList = () => {
 
   return (
     <div className={classes.root}>
-      <Loading loading={msg.startsWith("Loading")} />
+      <Loading loading={isLoading} />
       <TitleCard title="JOBS">
         Auto Refresh:
         <Switch
diff --git a/dashboard/client/src/pages/node/ClusterDetailInfoPage.tsx b/dashboard/client/src/pages/node/ClusterDetailInfoPage.tsx
index 06a69ea404b7b..8c8ecb211afc6 100644
--- a/dashboard/client/src/pages/node/ClusterDetailInfoPage.tsx
+++ b/dashboard/client/src/pages/node/ClusterDetailInfoPage.tsx
@@ -18,7 +18,7 @@ export const ClusterDetailInfoPage = () => {
   // TODO(aguo): Add more content to this page!
 
   const classes = useStyle();
-  const { clusterDetail, msg } = useClusterDetail();
+  const { clusterDetail, msg, isLoading } = useClusterDetail();
 
   if (!clusterDetail) {
     return (
@@ -30,7 +30,7 @@ export const ClusterDetailInfoPage = () => {
             path: undefined,
           }}
         />
-        <Loading loading={msg.startsWith("Loading")} />
+        <Loading loading={isLoading} />
         <TitleCard title={`CLUSTER`}>
           <StatusChip type="cluster" status="LOADING" />
           <br />
diff --git a/dashboard/client/src/pages/node/NodeDetail.tsx b/dashboard/client/src/pages/node/NodeDetail.tsx
index 6f357e44fc2e1..b54e41cd9ff18 100644
--- a/dashboard/client/src/pages/node/NodeDetail.tsx
+++ b/dashboard/client/src/pages/node/NodeDetail.tsx
@@ -43,6 +43,7 @@ const NodeDetailPage = () => {
     selectedTab,
     nodeDetail,
     msg,
+    isLoading,
     isRefreshing,
     onRefreshChange,
     raylet,
@@ -58,7 +59,7 @@ const NodeDetailPage = () => {
           path: `/cluster/nodes/${params.id}`,
         }}
       />
-      <Loading loading={msg.startsWith("Loading")} />
+      <Loading loading={isLoading} />
       <TitleCard title={`NODE - ${params.id}`}>
         <StatusChip
           type="node"
diff --git a/dashboard/client/src/pages/node/hook/useClusterDetail.ts b/dashboard/client/src/pages/node/hook/useClusterDetail.ts
index 7d0abc5d6ef60..8bfe35c44085f 100644
--- a/dashboard/client/src/pages/node/hook/useClusterDetail.ts
+++ b/dashboard/client/src/pages/node/hook/useClusterDetail.ts
@@ -6,7 +6,7 @@ import { getClusterMetadata } from "../../../service/global";
 export const useClusterDetail = () => {
   const [msg, setMsg] = useState("Loading the job detail");
   const [refreshing, setRefresh] = useState(true);
-  const { data: clusterDetail } = useSWR(
+  const { data: clusterDetail, isLoading } = useSWR(
     "useClusterDetail",
     async () => {
       try {
@@ -23,5 +23,6 @@ export const useClusterDetail = () => {
   return {
     clusterDetail,
     msg,
+    isLoading,
   };
 };
diff --git a/dashboard/client/src/pages/node/hook/useNodeDetail.ts b/dashboard/client/src/pages/node/hook/useNodeDetail.ts
index 3dd00e01818b0..5d06fba25446a 100644
--- a/dashboard/client/src/pages/node/hook/useNodeDetail.ts
+++ b/dashboard/client/src/pages/node/hook/useNodeDetail.ts
@@ -15,7 +15,7 @@ export const useNodeDetail = () => {
     setRefresh(event.target.checked);
   };
 
-  const { data: nodeDetail } = useSWR(
+  const { data: nodeDetail, isLoading } = useSWR(
     ["useNodeDetail", params.id],
     async ([_, nodeId]) => {
       const { data } = await getNodeDetail(nodeId);
@@ -47,6 +47,7 @@ export const useNodeDetail = () => {
     selectedTab,
     nodeDetail,
     msg,
+    isLoading,
     isRefreshing,
     onRefreshChange,
     raylet,
diff --git a/dashboard/client/src/pages/node/hook/useNodeList.ts b/dashboard/client/src/pages/node/hook/useNodeList.ts
index 3409138ad7122..ce9c764cf1a53 100644
--- a/dashboard/client/src/pages/node/hook/useNodeList.ts
+++ b/dashboard/client/src/pages/node/hook/useNodeList.ts
@@ -26,7 +26,7 @@ export const useNodeList = () => {
   const onSwitchChange = (event: React.ChangeEvent<HTMLInputElement>) => {
     setRefresh(event.target.checked);
   };
-  const { data } = useSWR(
+  const { data, isLoading } = useSWR(
     "useNodeList",
     async () => {
       const { data } = await getNodeList();
@@ -62,6 +62,7 @@ export const useNodeList = () => {
       filter.every((f) => node[f.key] && node[f.key].includes(f.val)),
     ),
     msg,
+    isLoading,
     isRefreshing,
     onSwitchChange,
     changeFilter,
diff --git a/dashboard/client/src/pages/node/index.tsx b/dashboard/client/src/pages/node/index.tsx
index 9f2f4bf8fc8ec..3753af2d22139 100644
--- a/dashboard/client/src/pages/node/index.tsx
+++ b/dashboard/client/src/pages/node/index.tsx
@@ -158,6 +158,7 @@ const Nodes = () => {
   const classes = useStyles();
   const {
     msg,
+    isLoading,
     isRefreshing,
     onSwitchChange,
     nodeList,
@@ -172,7 +173,7 @@ const Nodes = () => {
 
   return (
     <div className={classes.root}>
-      <Loading loading={msg.startsWith("Loading")} />
+      <Loading loading={isLoading} />
       <TitleCard title="NODES">
         Auto Refresh:
         <Switch



<!-- Thank you for your contribution! Please review https://github.com/ray-project/ray/blob/master/CONTRIBUTING.rst before opening a pull request. -->

<!-- Please add a reviewer to the assignee section when you create a PR. If you don't have the access to it, we will shortly find a reviewer and assign them to your PR. -->

## Why are these changes needed?
Previously, if a dashboard page was loading, it would grey out the whole screen and buttons would not be press-able. Now, we don't block out the whole page.
Also don't show loading bar if data is already loaded from in-memory cache.

<!-- Please give a short summary of the change and the problem this solves. -->

## Related issue number

<!-- For example: "Closes #1234" -->

## Checks

- [x] I've signed off every commit(by using the -s flag, i.e., `git commit -s`) in this PR.
- [x] I've run `scripts/format.sh` to lint the changes in this PR.
- [ ] I've included any doc changes needed for https://docs.ray.io/en/master/.
    - [ ] I've added any new APIs to the API Reference. For example, if I added a 
           method in Tune, I've added it in `doc/source/tune/api/` under the 
           corresponding `.rst` file.
- [ ] I've made sure the tests are passing. Note that there might be a few flaky tests, see the recent failures at https://flakey-tests.ray.io/
- Testing Strategy
   - [ ] Unit tests
   - [ ] Release tests
   - [ ] This PR is not tested :(
