This is an automated email from the ASF dual-hosted git repository.

ephraimanierobi pushed a commit to branch v2-3-test
in repository https://gitbox.apache.org/repos/asf/airflow.git

commit b433305a5c1d5584886e9d36d11f1ba0bdff65cf
Author: Brent Bovenzi <brent.bove...@gmail.com>
AuthorDate: Fri Apr 29 09:51:18 2022 -0400

    Store grid view selection in url params (#23290)
    
    * Add url params for dag_run_id and task_id
    
    * Persist other search params
    
    * simplify useSelection
    
    * delete extra params
    
    * remove API change
    
    (cherry picked from commit 4c1fcee6ba1559fcb53836ac7fcccd68b1891d93)
---
 airflow/www/package.json                           |  1 +
 airflow/www/static/js/tree/Tree.jsx                |  2 +-
 airflow/www/static/js/tree/api/useClearRun.js      |  2 +-
 airflow/www/static/js/tree/api/useClearTask.js     |  2 +-
 .../www/static/js/tree/api/useConfirmMarkTask.js   |  2 +-
 airflow/www/static/js/tree/api/useMarkFailedRun.js |  2 +-
 .../www/static/js/tree/api/useMarkFailedTask.js    |  2 +-
 .../www/static/js/tree/api/useMarkSuccessRun.js    |  2 +-
 .../www/static/js/tree/api/useMarkSuccessTask.js   |  2 +-
 airflow/www/static/js/tree/api/useQueueRun.js      |  2 +-
 airflow/www/static/js/tree/api/useRunTask.js       |  2 +-
 airflow/www/static/js/tree/api/useTasks.js         |  3 ++
 airflow/www/static/js/tree/api/useTreeData.js      |  4 +-
 airflow/www/static/js/tree/context/autorefresh.jsx |  2 +-
 airflow/www/static/js/tree/context/selection.jsx   | 56 ----------------------
 airflow/www/static/js/tree/dagRuns/index.jsx       |  2 +-
 airflow/www/static/js/tree/dagRuns/index.test.jsx  |  6 +--
 airflow/www/static/js/tree/details/Header.jsx      |  8 ++--
 airflow/www/static/js/tree/details/index.jsx       |  2 +-
 airflow/www/static/js/tree/index.jsx               |  6 +--
 airflow/www/static/js/tree/renderTaskRows.jsx      |  2 +-
 airflow/www/static/js/tree/renderTaskRows.test.jsx |  6 +--
 .../tree/{treeDataUtils.js => utils/treeData.js}   |  0
 .../static/js/tree/{ => utils}/useErrorToast.js    |  0
 airflow/www/static/js/tree/utils/useSelection.jsx  | 54 +++++++++++++++++++++
 airflow/www/yarn.lock                              | 29 +++++++++++
 26 files changed, 117 insertions(+), 84 deletions(-)

diff --git a/airflow/www/package.json b/airflow/www/package.json
index 56ae182eb1..bf9cc10dbe 100644
--- a/airflow/www/package.json
+++ b/airflow/www/package.json
@@ -95,6 +95,7 @@
     "react-dom": "^17.0.2",
     "react-icons": "^4.3.1",
     "react-query": "^3.34.16",
+    "react-router-dom": "^6.3.0",
     "react-table": "^7.7.0",
     "redoc": "^2.0.0-rc.63",
     "url-search-params-polyfill": "^8.1.0"
diff --git a/airflow/www/static/js/tree/Tree.jsx 
b/airflow/www/static/js/tree/Tree.jsx
index a8f39f6a30..1283d14c1b 100644
--- a/airflow/www/static/js/tree/Tree.jsx
+++ b/airflow/www/static/js/tree/Tree.jsx
@@ -39,7 +39,7 @@ import renderTaskRows from './renderTaskRows';
 import ResetRoot from './ResetRoot';
 import DagRuns from './dagRuns';
 import Details from './details';
-import { useSelection } from './context/selection';
+import useSelection from './utils/useSelection';
 import { useAutoRefresh } from './context/autorefresh';
 
 const sidePanelKey = 'hideSidePanel';
diff --git a/airflow/www/static/js/tree/api/useClearRun.js 
b/airflow/www/static/js/tree/api/useClearRun.js
index afcb620c3a..52dcb21e5c 100644
--- a/airflow/www/static/js/tree/api/useClearRun.js
+++ b/airflow/www/static/js/tree/api/useClearRun.js
@@ -21,7 +21,7 @@ import axios from 'axios';
 import { useMutation, useQueryClient } from 'react-query';
 import { getMetaValue } from '../../utils';
 import { useAutoRefresh } from '../context/autorefresh';
-import useErrorToast from '../useErrorToast';
+import useErrorToast from '../utils/useErrorToast';
 
 const csrfToken = getMetaValue('csrf_token');
 const clearRunUrl = getMetaValue('dagrun_clear_url');
diff --git a/airflow/www/static/js/tree/api/useClearTask.js 
b/airflow/www/static/js/tree/api/useClearTask.js
index 777a621aaf..0733b402b4 100644
--- a/airflow/www/static/js/tree/api/useClearTask.js
+++ b/airflow/www/static/js/tree/api/useClearTask.js
@@ -21,7 +21,7 @@ import axios from 'axios';
 import { useMutation, useQueryClient } from 'react-query';
 import { getMetaValue } from '../../utils';
 import { useAutoRefresh } from '../context/autorefresh';
-import useErrorToast from '../useErrorToast';
+import useErrorToast from '../utils/useErrorToast';
 
 const csrfToken = getMetaValue('csrf_token');
 const clearUrl = getMetaValue('clear_url');
diff --git a/airflow/www/static/js/tree/api/useConfirmMarkTask.js 
b/airflow/www/static/js/tree/api/useConfirmMarkTask.js
index 7db31ab8a8..a095f5a674 100644
--- a/airflow/www/static/js/tree/api/useConfirmMarkTask.js
+++ b/airflow/www/static/js/tree/api/useConfirmMarkTask.js
@@ -20,7 +20,7 @@
 import axios from 'axios';
 import { useMutation } from 'react-query';
 import { getMetaValue } from '../../utils';
-import useErrorToast from '../useErrorToast';
+import useErrorToast from '../utils/useErrorToast';
 
 const confirmUrl = getMetaValue('confirm_url');
 
diff --git a/airflow/www/static/js/tree/api/useMarkFailedRun.js 
b/airflow/www/static/js/tree/api/useMarkFailedRun.js
index c7487be5e9..697a49aa4d 100644
--- a/airflow/www/static/js/tree/api/useMarkFailedRun.js
+++ b/airflow/www/static/js/tree/api/useMarkFailedRun.js
@@ -21,7 +21,7 @@ import axios from 'axios';
 import { useMutation, useQueryClient } from 'react-query';
 import { getMetaValue } from '../../utils';
 import { useAutoRefresh } from '../context/autorefresh';
-import useErrorToast from '../useErrorToast';
+import useErrorToast from '../utils/useErrorToast';
 
 const csrfToken = getMetaValue('csrf_token');
 const markFailedUrl = getMetaValue('dagrun_failed_url');
diff --git a/airflow/www/static/js/tree/api/useMarkFailedTask.js 
b/airflow/www/static/js/tree/api/useMarkFailedTask.js
index 4ef72df1ba..3c8f14c7d0 100644
--- a/airflow/www/static/js/tree/api/useMarkFailedTask.js
+++ b/airflow/www/static/js/tree/api/useMarkFailedTask.js
@@ -21,7 +21,7 @@ import axios from 'axios';
 import { useMutation, useQueryClient } from 'react-query';
 import { getMetaValue } from '../../utils';
 import { useAutoRefresh } from '../context/autorefresh';
-import useErrorToast from '../useErrorToast';
+import useErrorToast from '../utils/useErrorToast';
 
 const failedUrl = getMetaValue('failed_url');
 const csrfToken = getMetaValue('csrf_token');
diff --git a/airflow/www/static/js/tree/api/useMarkSuccessRun.js 
b/airflow/www/static/js/tree/api/useMarkSuccessRun.js
index 6076e6ba1e..0d171e2ed8 100644
--- a/airflow/www/static/js/tree/api/useMarkSuccessRun.js
+++ b/airflow/www/static/js/tree/api/useMarkSuccessRun.js
@@ -21,7 +21,7 @@ import axios from 'axios';
 import { useMutation, useQueryClient } from 'react-query';
 import { getMetaValue } from '../../utils';
 import { useAutoRefresh } from '../context/autorefresh';
-import useErrorToast from '../useErrorToast';
+import useErrorToast from '../utils/useErrorToast';
 
 const markSuccessUrl = getMetaValue('dagrun_success_url');
 const csrfToken = getMetaValue('csrf_token');
diff --git a/airflow/www/static/js/tree/api/useMarkSuccessTask.js 
b/airflow/www/static/js/tree/api/useMarkSuccessTask.js
index 14d83e653d..41d68fe24c 100644
--- a/airflow/www/static/js/tree/api/useMarkSuccessTask.js
+++ b/airflow/www/static/js/tree/api/useMarkSuccessTask.js
@@ -21,7 +21,7 @@ import axios from 'axios';
 import { useMutation, useQueryClient } from 'react-query';
 import { getMetaValue } from '../../utils';
 import { useAutoRefresh } from '../context/autorefresh';
-import useErrorToast from '../useErrorToast';
+import useErrorToast from '../utils/useErrorToast';
 
 const csrfToken = getMetaValue('csrf_token');
 const successUrl = getMetaValue('success_url');
diff --git a/airflow/www/static/js/tree/api/useQueueRun.js 
b/airflow/www/static/js/tree/api/useQueueRun.js
index 5848d680f9..0acae34110 100644
--- a/airflow/www/static/js/tree/api/useQueueRun.js
+++ b/airflow/www/static/js/tree/api/useQueueRun.js
@@ -21,7 +21,7 @@ import axios from 'axios';
 import { useMutation, useQueryClient } from 'react-query';
 import { getMetaValue } from '../../utils';
 import { useAutoRefresh } from '../context/autorefresh';
-import useErrorToast from '../useErrorToast';
+import useErrorToast from '../utils/useErrorToast';
 
 const csrfToken = getMetaValue('csrf_token');
 const queuedUrl = getMetaValue('dagrun_queued_url');
diff --git a/airflow/www/static/js/tree/api/useRunTask.js 
b/airflow/www/static/js/tree/api/useRunTask.js
index 810b2dceab..4616d2b123 100644
--- a/airflow/www/static/js/tree/api/useRunTask.js
+++ b/airflow/www/static/js/tree/api/useRunTask.js
@@ -21,7 +21,7 @@ import axios from 'axios';
 import { useMutation, useQueryClient } from 'react-query';
 import { getMetaValue } from '../../utils';
 import { useAutoRefresh } from '../context/autorefresh';
-import useErrorToast from '../useErrorToast';
+import useErrorToast from '../utils/useErrorToast';
 
 const csrfToken = getMetaValue('csrf_token');
 const runUrl = getMetaValue('run_url');
diff --git a/airflow/www/static/js/tree/api/useTasks.js 
b/airflow/www/static/js/tree/api/useTasks.js
index 86192ece5c..9d0f56d7cb 100644
--- a/airflow/www/static/js/tree/api/useTasks.js
+++ b/airflow/www/static/js/tree/api/useTasks.js
@@ -27,5 +27,8 @@ export default function useTasks(dagId) {
   return useQuery(
     ['tasks', dagId],
     () => axios.get(tasksUrl),
+    {
+      placeholderData: { tasks: [] },
+    },
   );
 }
diff --git a/airflow/www/static/js/tree/api/useTreeData.js 
b/airflow/www/static/js/tree/api/useTreeData.js
index 84886249c4..9cb9737fd3 100644
--- a/airflow/www/static/js/tree/api/useTreeData.js
+++ b/airflow/www/static/js/tree/api/useTreeData.js
@@ -24,8 +24,8 @@ import axios from 'axios';
 
 import { getMetaValue } from '../../utils';
 import { useAutoRefresh } from '../context/autorefresh';
-import { formatData, areActiveRuns } from '../treeDataUtils';
-import useErrorToast from '../useErrorToast';
+import { formatData, areActiveRuns } from '../utils/treeData';
+import useErrorToast from '../utils/useErrorToast';
 
 // dagId comes from dag.html
 const dagId = getMetaValue('dag_id');
diff --git a/airflow/www/static/js/tree/context/autorefresh.jsx 
b/airflow/www/static/js/tree/context/autorefresh.jsx
index 77ca7f342f..dc21552028 100644
--- a/airflow/www/static/js/tree/context/autorefresh.jsx
+++ b/airflow/www/static/js/tree/context/autorefresh.jsx
@@ -21,7 +21,7 @@
 
 import React, { useContext, useState, useEffect } from 'react';
 import { getMetaValue } from '../../utils';
-import { formatData, areActiveRuns } from '../treeDataUtils';
+import { formatData, areActiveRuns } from '../utils/treeData';
 
 const autoRefreshKey = 'disabledAutoRefresh';
 
diff --git a/airflow/www/static/js/tree/context/selection.jsx 
b/airflow/www/static/js/tree/context/selection.jsx
deleted file mode 100644
index 29fcece223..0000000000
--- a/airflow/www/static/js/tree/context/selection.jsx
+++ /dev/null
@@ -1,56 +0,0 @@
-/*!
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import React, { useContext, useReducer } from 'react';
-
-const SelectionContext = React.createContext(null);
-
-const SELECT = 'SELECT';
-const DESELECT = 'DESELECT';
-
-const selectionReducer = (state, { type, payload }) => {
-  switch (type) {
-    case SELECT:
-      // Deselect if it is the same selection
-      if (payload.taskId === state.taskId && payload.runId === state.runId) {
-        return {};
-      }
-      return payload;
-    case DESELECT:
-      return {};
-    default:
-      return state;
-  }
-};
-
-// Expose the grid selection to any react component instead of passing around 
lots of props
-export const SelectionProvider = ({ children }) => {
-  const [selected, dispatch] = useReducer(selectionReducer, {});
-
-  const clearSelection = () => dispatch({ type: DESELECT });
-  const onSelect = (payload) => dispatch({ type: SELECT, payload });
-
-  return (
-    <SelectionContext.Provider value={{ selected, clearSelection, onSelect }}>
-      {children}
-    </SelectionContext.Provider>
-  );
-};
-
-export const useSelection = () => useContext(SelectionContext);
diff --git a/airflow/www/static/js/tree/dagRuns/index.jsx 
b/airflow/www/static/js/tree/dagRuns/index.jsx
index 4632cede89..c304b18bcf 100644
--- a/airflow/www/static/js/tree/dagRuns/index.jsx
+++ b/airflow/www/static/js/tree/dagRuns/index.jsx
@@ -29,7 +29,7 @@ import {
 import { useTreeData } from '../api';
 import DagRunBar from './Bar';
 import { getDuration, formatDuration } from '../../datetime_utils';
-import { useSelection } from '../context/selection';
+import useSelection from '../utils/useSelection';
 
 const DurationTick = ({ children, ...rest }) => (
   <Text fontSize="sm" color="gray.400" right={1} position="absolute" 
whiteSpace="nowrap" {...rest}>
diff --git a/airflow/www/static/js/tree/dagRuns/index.test.jsx 
b/airflow/www/static/js/tree/dagRuns/index.test.jsx
index 5faa8b6e95..15a30ce41a 100644
--- a/airflow/www/static/js/tree/dagRuns/index.test.jsx
+++ b/airflow/www/static/js/tree/dagRuns/index.test.jsx
@@ -24,10 +24,10 @@ import { render } from '@testing-library/react';
 import { ChakraProvider, Table, Tbody } from '@chakra-ui/react';
 import moment from 'moment-timezone';
 import { QueryClient, QueryClientProvider } from 'react-query';
+import { MemoryRouter } from 'react-router-dom';
 
 import DagRuns from './index';
 import { ContainerRefProvider } from '../context/containerRef';
-import { SelectionProvider } from '../context/selection';
 import { TimezoneProvider } from '../context/timezone';
 import { AutoRefreshProvider } from '../context/autorefresh';
 
@@ -42,13 +42,13 @@ const Wrapper = ({ children }) => {
           <ContainerRefProvider value={{}}>
             <TimezoneProvider value={{ timezone: 'UTC' }}>
               <AutoRefreshProvider value={{ isRefreshOn: false, stopRefresh: 
() => {} }}>
-                <SelectionProvider value={{ onSelect: () => {}, selected: {} 
}}>
+                <MemoryRouter>
                   <Table>
                     <Tbody>
                       {children}
                     </Tbody>
                   </Table>
-                </SelectionProvider>
+                </MemoryRouter>
               </AutoRefreshProvider>
             </TimezoneProvider>
           </ContainerRefProvider>
diff --git a/airflow/www/static/js/tree/details/Header.jsx 
b/airflow/www/static/js/tree/details/Header.jsx
index 14187f199e..5ce3b0583d 100644
--- a/airflow/www/static/js/tree/details/Header.jsx
+++ b/airflow/www/static/js/tree/details/Header.jsx
@@ -29,9 +29,9 @@ import {
 import { MdPlayArrow } from 'react-icons/md';
 
 import { getMetaValue } from '../../utils';
-import { useSelection } from '../context/selection';
+import useSelection from '../utils/useSelection';
 import Time from '../Time';
-import { useTreeData } from '../api';
+import { useTasks, useTreeData } from '../api';
 
 const dagId = getMetaValue('dag_id');
 
@@ -44,8 +44,10 @@ const LabelValue = ({ label, value }) => (
 
 const Header = () => {
   const { data: { dagRuns = [] } } = useTreeData();
-  const { selected: { taskId, runId, task }, onSelect, clearSelection } = 
useSelection();
+  const { selected: { taskId, runId }, onSelect, clearSelection } = 
useSelection();
+  const { data: { tasks } } = useTasks();
   const dagRun = dagRuns.find((r) => r.runId === runId);
+  const task = tasks.find((t) => t.taskId === taskId);
 
   let runLabel;
   if (dagRun) {
diff --git a/airflow/www/static/js/tree/details/index.jsx 
b/airflow/www/static/js/tree/details/index.jsx
index ffe8b0ff74..ee0fef2dd9 100644
--- a/airflow/www/static/js/tree/details/index.jsx
+++ b/airflow/www/static/js/tree/details/index.jsx
@@ -28,7 +28,7 @@ import Header from './Header';
 import TaskInstanceContent from './content/taskInstance';
 import DagRunContent from './content/dagRun';
 import DagContent from './content/Dag';
-import { useSelection } from '../context/selection';
+import useSelection from '../utils/useSelection';
 
 const Details = () => {
   const { selected } = useSelection();
diff --git a/airflow/www/static/js/tree/index.jsx 
b/airflow/www/static/js/tree/index.jsx
index 4db274f117..abe135bebb 100644
--- a/airflow/www/static/js/tree/index.jsx
+++ b/airflow/www/static/js/tree/index.jsx
@@ -21,13 +21,13 @@
 
 import React from 'react';
 import ReactDOM from 'react-dom';
+import { BrowserRouter } from 'react-router-dom';
 import { ChakraProvider, extendTheme } from '@chakra-ui/react';
 import { CacheProvider } from '@emotion/react';
 import createCache from '@emotion/cache';
 import { QueryClient, QueryClientProvider } from 'react-query';
 
 import Tree from './Tree';
-import { SelectionProvider } from './context/selection';
 import { ContainerRefProvider } from './context/containerRef';
 import { TimezoneProvider } from './context/timezone';
 import { AutoRefreshProvider } from './context/autorefresh';
@@ -77,9 +77,9 @@ function App() {
             <QueryClientProvider client={queryClient}>
               <TimezoneProvider>
                 <AutoRefreshProvider>
-                  <SelectionProvider>
+                  <BrowserRouter>
                     <Tree />
-                  </SelectionProvider>
+                  </BrowserRouter>
                 </AutoRefreshProvider>
               </TimezoneProvider>
             </QueryClientProvider>
diff --git a/airflow/www/static/js/tree/renderTaskRows.jsx 
b/airflow/www/static/js/tree/renderTaskRows.jsx
index 36fc64c195..8713c3801d 100644
--- a/airflow/www/static/js/tree/renderTaskRows.jsx
+++ b/airflow/www/static/js/tree/renderTaskRows.jsx
@@ -34,7 +34,7 @@ import StatusBox, { boxSize, boxSizePx } from './StatusBox';
 import TaskName from './TaskName';
 
 import { getMetaValue } from '../utils';
-import { useSelection } from './context/selection';
+import useSelection from './utils/useSelection';
 
 const boxPadding = 3;
 const boxPaddingPx = `${boxPadding}px`;
diff --git a/airflow/www/static/js/tree/renderTaskRows.test.jsx 
b/airflow/www/static/js/tree/renderTaskRows.test.jsx
index b163d62363..88afb4a580 100644
--- a/airflow/www/static/js/tree/renderTaskRows.test.jsx
+++ b/airflow/www/static/js/tree/renderTaskRows.test.jsx
@@ -24,10 +24,10 @@ import { render, fireEvent } from '@testing-library/react';
 import { ChakraProvider, Table, Tbody } from '@chakra-ui/react';
 import moment from 'moment';
 import { QueryClient, QueryClientProvider } from 'react-query';
+import { MemoryRouter } from 'react-router-dom';
 
 import renderTaskRows from './renderTaskRows';
 import { ContainerRefProvider } from './context/containerRef';
-import { SelectionProvider } from './context/selection';
 
 global.moment = moment;
 
@@ -101,13 +101,13 @@ const Wrapper = ({ children }) => {
       <ChakraProvider>
         <QueryClientProvider client={queryClient}>
           <ContainerRefProvider value={{}}>
-            <SelectionProvider value={{ onSelect: () => {}, selected: {} }}>
+            <MemoryRouter>
               <Table>
                 <Tbody>
                   {children}
                 </Tbody>
               </Table>
-            </SelectionProvider>
+            </MemoryRouter>
           </ContainerRefProvider>
         </QueryClientProvider>
       </ChakraProvider>
diff --git a/airflow/www/static/js/tree/treeDataUtils.js 
b/airflow/www/static/js/tree/utils/treeData.js
similarity index 100%
rename from airflow/www/static/js/tree/treeDataUtils.js
rename to airflow/www/static/js/tree/utils/treeData.js
diff --git a/airflow/www/static/js/tree/useErrorToast.js 
b/airflow/www/static/js/tree/utils/useErrorToast.js
similarity index 100%
rename from airflow/www/static/js/tree/useErrorToast.js
rename to airflow/www/static/js/tree/utils/useErrorToast.js
diff --git a/airflow/www/static/js/tree/utils/useSelection.jsx 
b/airflow/www/static/js/tree/utils/useSelection.jsx
new file mode 100644
index 0000000000..c7220b4f21
--- /dev/null
+++ b/airflow/www/static/js/tree/utils/useSelection.jsx
@@ -0,0 +1,54 @@
+/*!
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import { useSearchParams } from 'react-router-dom';
+
+const RUN_ID = 'dag_run_id';
+const TASK_ID = 'task_id';
+
+const useSelection = () => {
+  const [searchParams, setSearchParams] = useSearchParams();
+
+  // Clear selection, but keep other search params
+  const clearSelection = () => {
+    searchParams.delete(RUN_ID);
+    searchParams.delete(TASK_ID);
+    setSearchParams(searchParams);
+  };
+
+  const onSelect = (payload) => {
+    const params = new URLSearchParams(searchParams);
+
+    if (payload.runId) params.set(RUN_ID, payload.runId);
+    else params.delete(RUN_ID);
+
+    if (payload.taskId) params.set(TASK_ID, payload.taskId);
+    else params.delete(TASK_ID);
+
+    setSearchParams(params);
+  };
+
+  const runId = searchParams.get(RUN_ID);
+  const taskId = searchParams.get(TASK_ID);
+  const selected = { runId, taskId };
+
+  return { selected, clearSelection, onSelect };
+};
+
+export default useSelection;
diff --git a/airflow/www/yarn.lock b/airflow/www/yarn.lock
index 04bc350e31..f342f70deb 100644
--- a/airflow/www/yarn.lock
+++ b/airflow/www/yarn.lock
@@ -1298,6 +1298,13 @@
   dependencies:
     regenerator-runtime "^0.13.4"
 
+"@babel/runtime@^7.7.6":
+  version "7.17.9"
+  resolved 
"https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.17.9.tgz#d19fbf802d01a8cb6cf053a64e472d42c434ba72";
+  integrity 
sha512-lSiBBvodq29uShpWGNbgFdKYNiFDo5/HIYsaCEY9ff4sb10x9jizo2+pRrSyF4jKZCXqgzuqBOQKbUm90gQwJg==
+  dependencies:
+    regenerator-runtime "^0.13.4"
+
 "@babel/template@^7.14.5":
   version "7.14.5"
   resolved 
"https://registry.yarnpkg.com/@babel/template/-/template-7.14.5.tgz#a9bc9d8b33354ff6e55a9c60d1109200a68974f4";
@@ -6473,6 +6480,13 @@ hey-listen@^1.0.8:
   resolved 
"https://registry.yarnpkg.com/hey-listen/-/hey-listen-1.0.8.tgz#8e59561ff724908de1aa924ed6ecc84a56a9aa68";
   integrity 
sha512-COpmrF2NOg4TBWUJ5UVyaCU2A88wEMkUPK4hNqyCkqHbxT92BbvfjoSozkAIIm6XhicGlJHhFdullInrdhwU8Q==
 
+history@^5.2.0:
+  version "5.3.0"
+  resolved 
"https://registry.yarnpkg.com/history/-/history-5.3.0.tgz#1548abaa245ba47992f063a0783db91ef201c73b";
+  integrity 
sha512-ZqaKwjjrAYUYfLG+htGaIIZ4nioX2L70ZUMIFysS3xvBsSG4x/n1V6TXV3N8ZYNuFGlDirFg32T7B6WOUPDYcQ==
+  dependencies:
+    "@babel/runtime" "^7.7.6"
+
 hmac-drbg@^1.0.1:
   version "1.0.1"
   resolved 
"https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1";
@@ -9829,6 +9843,21 @@ react-remove-scroll@2.4.1:
     use-callback-ref "^1.2.3"
     use-sidecar "^1.0.1"
 
+react-router-dom@^6.3.0:
+  version "6.3.0"
+  resolved 
"https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-6.3.0.tgz#a0216da813454e521905b5fa55e0e5176123f43d";
+  integrity 
sha512-uaJj7LKytRxZNQV8+RbzJWnJ8K2nPsOOEuX7aQstlMZKQT0164C+X2w6bnkqU3sjtLvpd5ojrezAyfZ1+0sStw==
+  dependencies:
+    history "^5.2.0"
+    react-router "6.3.0"
+
+react-router@6.3.0:
+  version "6.3.0"
+  resolved 
"https://registry.yarnpkg.com/react-router/-/react-router-6.3.0.tgz#3970cc64b4cb4eae0c1ea5203a80334fdd175557";
+  integrity 
sha512-7Wh1DzVQ+tlFjkeo+ujvjSqSJmkt1+8JO+T5xklPlgrh70y7ogx75ODRW0ThWhY7S+6yEDks8TYrtQe/aoboBQ==
+  dependencies:
+    history "^5.2.0"
+
 react-style-singleton@^2.1.0:
   version "2.1.1"
   resolved 
"https://registry.yarnpkg.com/react-style-singleton/-/react-style-singleton-2.1.1.tgz#ce7f90b67618be2b6b94902a30aaea152ce52e66";

Reply via email to