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"