Add react-router-dom-v5-compat (#159173)

## Summary

Prep work for bumping react-router to v6
Following https://github.com/remix-run/react-router/discussions/8753

---------

Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Patryk Kopyciński 2023-06-14 14:13:15 +02:00 committed by GitHub
parent 2fba1b651e
commit 09577fa0af
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
42 changed files with 715 additions and 613 deletions

View file

@ -8,6 +8,7 @@
import React from 'react';
import { BrowserRouter as Router, Redirect, Switch } from 'react-router-dom';
import { CompatRouter } from 'react-router-dom-v5-compat';
import { Route } from '@kbn/shared-ux-router';
import { EuiPage } from '@elastic/eui';
import { useDeps } from '../../hooks/use_deps';
@ -26,13 +27,15 @@ export const App: React.FC = () => {
return (
<Router basename={appBasePath}>
<EuiPage>
<Sidebar />
<Switch>
{routeElements}
<Redirect to="/count-until" />
</Switch>
</EuiPage>
<CompatRouter>
<EuiPage>
<Sidebar />
<Switch>
{routeElements}
<Redirect to="/count-until" />
</Switch>
</EuiPage>
</CompatRouter>
</Router>
);
};

View file

@ -10,6 +10,7 @@ import React from 'react';
import ReactDOM from 'react-dom';
// eslint-disable-next-line no-restricted-imports
import { Router, Switch, Route, Redirect } from 'react-router-dom';
import { CompatRouter } from 'react-router-dom-v5-compat';
import { RedirectAppLinks } from '@kbn/shared-ux-link-redirect-app';
import { EuiPageTemplate, EuiSideNav } from '@elastic/eui';
import { AppMountParameters, CoreStart } from '@kbn/core/public';
@ -24,50 +25,52 @@ export const renderApp = (
) => {
ReactDOM.render(
<Router history={history}>
<RedirectAppLinks coreStart={core}>
<EuiPageTemplate offset={0}>
<EuiPageTemplate.Sidebar>
<EuiSideNav
items={[
{
id: 'Examples',
name: 'Examples',
items: [
{
id: 'todos',
name: 'Todo app',
'data-test-subj': 'todosExample',
href: '/app/contentManagementExamples/todos',
},
{
id: 'msearch',
name: 'MSearch',
'data-test-subj': 'msearchExample',
href: '/app/contentManagementExamples/msearch',
},
],
},
]}
/>
</EuiPageTemplate.Sidebar>
<CompatRouter>
<RedirectAppLinks coreStart={core}>
<EuiPageTemplate offset={0}>
<EuiPageTemplate.Sidebar>
<EuiSideNav
items={[
{
id: 'Examples',
name: 'Examples',
items: [
{
id: 'todos',
name: 'Todo app',
'data-test-subj': 'todosExample',
href: '/app/contentManagementExamples/todos',
},
{
id: 'msearch',
name: 'MSearch',
'data-test-subj': 'msearchExample',
href: '/app/contentManagementExamples/msearch',
},
],
},
]}
/>
</EuiPageTemplate.Sidebar>
<EuiPageTemplate.Section>
<Switch>
<Redirect from="/" to="/todos" exact />
<Route path="/todos">
<TodoApp contentClient={contentManagement.client} />
</Route>
<Route path="/msearch">
<MSearchApp
contentClient={contentManagement.client}
core={core}
savedObjectsTagging={savedObjectsTaggingOss}
/>
</Route>
</Switch>
</EuiPageTemplate.Section>
</EuiPageTemplate>
</RedirectAppLinks>
<EuiPageTemplate.Section>
<Switch>
<Redirect from="/" to="/todos" exact />
<Route path="/todos">
<TodoApp contentClient={contentManagement.client} />
</Route>
<Route path="/msearch">
<MSearchApp
contentClient={contentManagement.client}
core={core}
savedObjectsTagging={savedObjectsTaggingOss}
/>
</Route>
</Switch>
</EuiPageTemplate.Section>
</EuiPageTemplate>
</RedirectAppLinks>
</CompatRouter>
</Router>,
element
);

View file

@ -9,6 +9,7 @@
import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter as Router, withRouter, RouteComponentProps } from 'react-router-dom';
import { CompatRouter } from 'react-router-dom-v5-compat';
import { Route } from '@kbn/shared-ux-router';
import { EuiPage, EuiPageSideBar_Deprecated as EuiPageSideBar, EuiSideNav } from '@elastic/eui';
@ -126,12 +127,14 @@ const EmbeddableExplorerApp = ({
return (
<Router basename={basename}>
<EuiPage>
<EuiPageSideBar>
<Nav navigateToApp={navigateToApp} pages={pages} />
</EuiPageSideBar>
{routes}
</EuiPage>
<CompatRouter>
<EuiPage>
<EuiPageSideBar>
<Nav navigateToApp={navigateToApp} pages={pages} />
</EuiPageSideBar>
{routes}
</EuiPage>
</CompatRouter>
</Router>
);
};

View file

@ -9,6 +9,7 @@
import React from 'react';
import { FormattedMessage, I18nProvider } from '@kbn/i18n-react';
import { Router, Switch } from 'react-router-dom';
import { CompatRouter } from 'react-router-dom-v5-compat';
import { Route } from '@kbn/shared-ux-router';
import { EuiPageTemplate } from '@elastic/eui';
@ -45,27 +46,25 @@ export const GuidedOnboardingExampleApp = (props: GuidedOnboardingExampleAppDeps
{guidedOnboarding.guidedOnboardingApi?.isEnabled ? (
<EuiPageTemplate.Section>
<Router history={history}>
<Switch>
<Route exact path="/">
<Main notifications={notifications} guidedOnboarding={guidedOnboarding} />
</Route>
<Route exact path="/stepOne">
<StepOne guidedOnboarding={guidedOnboarding} />
</Route>
<Route exact path="/stepTwo">
<StepTwo />
</Route>
<Route exact path="/stepThree">
<StepThree guidedOnboarding={guidedOnboarding} />
</Route>
p
<Route
path="/stepFour/:indexName?"
render={(routeProps) => (
<StepFour guidedOnboarding={guidedOnboarding} {...routeProps} />
)}
/>
</Switch>
<CompatRouter>
<Switch>
<Route exact path="/">
<Main notifications={notifications} guidedOnboarding={guidedOnboarding} />
</Route>
<Route exact path="/stepOne">
<StepOne guidedOnboarding={guidedOnboarding} />
</Route>
<Route exact path="/stepTwo">
<StepTwo />
</Route>
<Route exact path="/stepThree">
<StepThree guidedOnboarding={guidedOnboarding} />
</Route>
<Route path="/stepFour/:indexName?">
<StepFour guidedOnboarding={guidedOnboarding} />
</Route>
</Switch>
</CompatRouter>
</Router>
</EuiPageTemplate.Section>
) : (

View file

@ -17,19 +17,16 @@ import {
EuiPageContentBody_Deprecated as EuiPageContentBody,
EuiCode,
} from '@elastic/eui';
import { RouteComponentProps } from 'react-router-dom';
import { useParams } from 'react-router-dom';
interface StepFourProps {
guidedOnboarding: GuidedOnboardingPluginStart;
}
export const StepFour = (props: StepFourProps & RouteComponentProps<{ indexName: string }>) => {
const {
guidedOnboarding: { guidedOnboardingApi },
match: {
params: { indexName },
},
} = props;
export const StepFour: React.FC<StepFourProps> = ({
guidedOnboarding: { guidedOnboardingApi },
}) => {
const { indexName } = useParams<{ indexName: string }>();
const [, setIsTourStepOpen] = useState<boolean>(false);

View file

@ -10,6 +10,7 @@ import ReactDOM from 'react-dom';
import React, { useMemo } from 'react';
import { useAsync } from 'react-use/lib';
import { Router, Redirect, Switch } from 'react-router-dom';
import { CompatRouter } from 'react-router-dom-v5-compat';
import { Route } from '@kbn/shared-ux-router';
import { AppMountParameters } from '@kbn/core/public';
@ -49,17 +50,19 @@ const PortableDashboardsDemos = ({
}) => {
return (
<Router history={history}>
<Switch>
<Route exact path="/">
<Redirect to={DASHBOARD_DEMO_PATH} />
</Route>
<Route path={DASHBOARD_LIST_PATH}>
<PortableDashboardListingDemo history={history} />
</Route>
<Route path={DASHBOARD_DEMO_PATH}>
<DashboardsDemo data={data} dashboard={dashboard} history={history} />
</Route>
</Switch>
<CompatRouter>
<Switch>
<Route exact path="/">
<Redirect to={DASHBOARD_DEMO_PATH} />
</Route>
<Route path={DASHBOARD_LIST_PATH}>
<PortableDashboardListingDemo history={history} />
</Route>
<Route path={DASHBOARD_DEMO_PATH}>
<DashboardsDemo data={data} dashboard={dashboard} history={history} />
</Route>
</Switch>
</CompatRouter>
</Router>
);
};

View file

@ -8,6 +8,7 @@
import React from 'react';
import { BrowserRouter as Router, Redirect, Switch } from 'react-router-dom';
import { CompatRouter } from 'react-router-dom-v5-compat';
import { Route } from '@kbn/shared-ux-router';
import { EuiPage } from '@elastic/eui';
import { useDeps } from '../../hooks/use_deps';
@ -26,13 +27,15 @@ export const App: React.FC = () => {
return (
<Router basename={appBasePath}>
<EuiPage>
<Sidebar />
<Switch>
{routeElements}
<Redirect to="/simple-string-stream" />
</Switch>
</EuiPage>
<CompatRouter>
<EuiPage>
<Sidebar />
<Switch>
{routeElements}
<Redirect to="/simple-string-stream" />
</Switch>
</EuiPage>
</CompatRouter>
</Router>
);
};

View file

@ -8,6 +8,7 @@
import React, { useEffect } from 'react';
import { BrowserRouter as Router } from 'react-router-dom';
import { CompatRouter } from 'react-router-dom-v5-compat';
import { FormattedMessage, I18nProvider } from '@kbn/i18n-react';
import {
@ -62,61 +63,63 @@ export const ScreenshotModeExampleApp = ({
}, [isScreenshotMode, notifications, http]);
return (
<Router basename={basename}>
<I18nProvider>
<>
<navigation.ui.TopNavMenu
appName={PLUGIN_NAME}
showSearchBar={true}
useDefaultBehaviors={true}
/>
<EuiPage restrictWidth="1000px">
<EuiPageBody>
<EuiPageHeader>
<EuiTitle size="l">
<h1>
<FormattedMessage
id="screenshotModeExample.helloWorldText"
defaultMessage="{name}"
values={{ name: PLUGIN_NAME }}
/>
</h1>
</EuiTitle>
</EuiPageHeader>
<EuiPageContent>
<EuiPageContentHeader>
<EuiTitle>
<h2>
{isScreenshotMode ? (
<FormattedMessage
id="screenshotModeExample.screenshotModeTitle"
defaultMessage="We are in screenshot mode!"
/>
) : (
<FormattedMessage
id="screenshotModeExample.normalModeTitle"
defaultMessage="We are not in screenshot mode!"
/>
)}
</h2>
<CompatRouter>
<I18nProvider>
<>
<navigation.ui.TopNavMenu
appName={PLUGIN_NAME}
showSearchBar={true}
useDefaultBehaviors={true}
/>
<EuiPage restrictWidth="1000px">
<EuiPageBody>
<EuiPageHeader>
<EuiTitle size="l">
<h1>
<FormattedMessage
id="screenshotModeExample.helloWorldText"
defaultMessage="{name}"
values={{ name: PLUGIN_NAME }}
/>
</h1>
</EuiTitle>
</EuiPageContentHeader>
<EuiPageContentBody>
<EuiText>
{isScreenshotMode ? (
<p>We detected screenshot mode. The chrome navbar should be hidden.</p>
) : (
<p>
This is how the app looks in normal mode. The chrome navbar should be
visible.
</p>
)}
</EuiText>
</EuiPageContentBody>
</EuiPageContent>
</EuiPageBody>
</EuiPage>
</>
</I18nProvider>
</EuiPageHeader>
<EuiPageContent>
<EuiPageContentHeader>
<EuiTitle>
<h2>
{isScreenshotMode ? (
<FormattedMessage
id="screenshotModeExample.screenshotModeTitle"
defaultMessage="We are in screenshot mode!"
/>
) : (
<FormattedMessage
id="screenshotModeExample.normalModeTitle"
defaultMessage="We are not in screenshot mode!"
/>
)}
</h2>
</EuiTitle>
</EuiPageContentHeader>
<EuiPageContentBody>
<EuiText>
{isScreenshotMode ? (
<p>We detected screenshot mode. The chrome navbar should be hidden.</p>
) : (
<p>
This is how the app looks in normal mode. The chrome navbar should be
visible.
</p>
)}
</EuiText>
</EuiPageContentBody>
</EuiPageContent>
</EuiPageBody>
</EuiPage>
</>
</I18nProvider>
</CompatRouter>
</Router>
);
};

View file

@ -9,6 +9,7 @@
import React from 'react';
import ReactDOM from 'react-dom';
import { Router, Redirect } from 'react-router-dom';
import { CompatRouter } from 'react-router-dom-v5-compat';
import { Route } from '@kbn/shared-ux-router';
import { I18nProvider } from '@kbn/i18n-react';
import { AppMountParameters, CoreStart } from '@kbn/core/public';
@ -48,30 +49,32 @@ export const renderApp = (
<RedirectAppLinks application={application}>
<SearchExamplePage exampleLinks={LINKS} basePath={http.basePath}>
<Router history={history}>
<Route path={LINKS[0].path}>
<SearchExamplesApp
notifications={notifications}
navigation={navigation}
data={data}
http={http}
unifiedSearch={unifiedSearch}
/>
</Route>
<Route path={LINKS[1].path}>
<SqlSearchExampleApp notifications={notifications} data={data} />
</Route>
<Route path={LINKS[2].path}>
<SearchSessionsExampleApp
navigation={navigation}
notifications={notifications}
data={data}
unifiedSearch={unifiedSearch}
/>
</Route>
<CompatRouter>
<Route path={LINKS[0].path}>
<SearchExamplesApp
notifications={notifications}
navigation={navigation}
data={data}
http={http}
unifiedSearch={unifiedSearch}
/>
</Route>
<Route path={LINKS[1].path}>
<SqlSearchExampleApp notifications={notifications} data={data} />
</Route>
<Route path={LINKS[2].path}>
<SearchSessionsExampleApp
navigation={navigation}
notifications={notifications}
data={data}
unifiedSearch={unifiedSearch}
/>
</Route>
<Route path="/" exact={true}>
<Redirect to={LINKS[0].path} />
</Route>
<Route path="/" exact={true}>
<Redirect to={LINKS[0].path} />
</Route>
</CompatRouter>
</Router>
</SearchExamplePage>
</RedirectAppLinks>

View file

@ -8,6 +8,7 @@
import React, { useEffect, useMemo } from 'react';
import { Link, Router, Switch, useLocation } from 'react-router-dom';
import { CompatRouter } from 'react-router-dom-v5-compat';
import { Route } from '@kbn/shared-ux-router';
import { History } from 'history';
import {
@ -185,57 +186,59 @@ export const TodoAppPage: React.FC<{
return (
<Router history={props.history}>
<EuiPageBody>
<EuiPageHeader>
<EuiPageHeaderSection>
<EuiTitle size="l">
<h1>{props.appTitle}</h1>
</EuiTitle>
<EuiSpacer />
<EuiText>
<p>
This is a simple TODO app that uses state containers and state syncing utils. It
stores state in the URL similar like Discover or Dashboard apps do. <br />
Play with the app and see how the state is persisted in the URL.
<br /> Undo/Redo with browser history also works.
</p>
</EuiText>
</EuiPageHeaderSection>
</EuiPageHeader>
<EuiPageContent>
<EuiPageContentBody>
<Switch>
<Route path={'/completed'}>
<TodoApp filter={'completed'} stateContainer={stateContainer} />
</Route>
<Route path={'/not-completed'}>
<TodoApp filter={'not-completed'} stateContainer={stateContainer} />
</Route>
<Route path={'/'}>
<TodoApp filter={null} stateContainer={stateContainer} />
</Route>
</Switch>
<EuiSpacer size={'xxl'} />
<EuiText size={'s'}>
<p>Most of kibana apps persist state in the URL in two ways:</p>
<ol>
<li>Expanded state in rison format</li>
<li>
Just a state hash. <br />
In the URL only the hash from the state is stored. The state itself is stored in
the sessionStorage. See `state:storeInSessionStorage` advanced option for more
context.
</li>
</ol>
<p>You can switch between these two mods:</p>
</EuiText>
<EuiSpacer />
<EuiButton onClick={() => setUseHashedUrl(!useHashedUrl)}>
{useHashedUrl ? 'Use Expanded State' : 'Use Hashed State'}
</EuiButton>
</EuiPageContentBody>
</EuiPageContent>
</EuiPageBody>
<CompatRouter>
<EuiPageBody>
<EuiPageHeader>
<EuiPageHeaderSection>
<EuiTitle size="l">
<h1>{props.appTitle}</h1>
</EuiTitle>
<EuiSpacer />
<EuiText>
<p>
This is a simple TODO app that uses state containers and state syncing utils. It
stores state in the URL similar like Discover or Dashboard apps do. <br />
Play with the app and see how the state is persisted in the URL.
<br /> Undo/Redo with browser history also works.
</p>
</EuiText>
</EuiPageHeaderSection>
</EuiPageHeader>
<EuiPageContent>
<EuiPageContentBody>
<Switch>
<Route path={'/completed'}>
<TodoApp filter={'completed'} stateContainer={stateContainer} />
</Route>
<Route path={'/not-completed'}>
<TodoApp filter={'not-completed'} stateContainer={stateContainer} />
</Route>
<Route path={'/'}>
<TodoApp filter={null} stateContainer={stateContainer} />
</Route>
</Switch>
<EuiSpacer size={'xxl'} />
<EuiText size={'s'}>
<p>Most of kibana apps persist state in the URL in two ways:</p>
<ol>
<li>Expanded state in rison format</li>
<li>
Just a state hash. <br />
In the URL only the hash from the state is stored. The state itself is stored in
the sessionStorage. See `state:storeInSessionStorage` advanced option for more
context.
</li>
</ol>
<p>You can switch between these two mods:</p>
</EuiText>
<EuiSpacer />
<EuiButton onClick={() => setUseHashedUrl(!useHashedUrl)}>
{useHashedUrl ? 'Use Expanded State' : 'Use Hashed State'}
</EuiButton>
</EuiPageContentBody>
</EuiPageContent>
</EuiPageBody>
</CompatRouter>
</Router>
);
};

View file

@ -9,6 +9,7 @@
import React, { useEffect, useMemo, useState } from 'react';
import { History } from 'history';
import { Router } from 'react-router-dom';
import { CompatRouter } from 'react-router-dom-v5-compat';
import {
EuiFieldText,
@ -79,7 +80,7 @@ export const App = ({
return (
<StateContainersExamplesPage navigateToApp={navigateToApp} exampleLinks={exampleLinks}>
<Router history={history}>
<>
<CompatRouter>
<EuiPageBody>
<EuiPageHeader>
<EuiTitle size="l">
@ -114,7 +115,7 @@ export const App = ({
/>
</EuiPageContent>
</EuiPageBody>
</>
</CompatRouter>
</Router>
</StateContainersExamplesPage>
);

View file

@ -933,9 +933,10 @@
"react-resizable": "^3.0.4",
"react-resize-detector": "^7.1.1",
"react-reverse-portal": "^2.1.0",
"react-router": "^5.2.0",
"react-router": "^5.3.4",
"react-router-config": "^5.1.1",
"react-router-dom": "^5.2.0",
"react-router-dom": "^5.3.4",
"react-router-dom-v5-compat": "^6.12.0",
"react-shortcuts": "^2.1.0",
"react-syntax-highlighter": "^15.3.1",
"react-use": "^15.3.8",
@ -1305,9 +1306,9 @@
"@types/react-grid-layout": "^1.3.2",
"@types/react-intl": "^2.3.15",
"@types/react-is": "^17.0.3",
"@types/react-router": "^5.1.7",
"@types/react-router-config": "^5.0.2",
"@types/react-router-dom": "^5.1.5",
"@types/react-router": "^5.1.20",
"@types/react-router-config": "^5.0.7",
"@types/react-router-dom": "^5.3.3",
"@types/react-syntax-highlighter": "^15.4.0",
"@types/react-test-renderer": "^17.0.2",
"@types/react-virtualized": "^9.21.22",

View file

@ -9,6 +9,7 @@
import React, { FunctionComponent, useMemo } from 'react';
// eslint-disable-next-line no-restricted-imports
import { RouteComponentProps, Router, Route, Switch } from 'react-router-dom';
import { CompatRouter } from 'react-router-dom-v5-compat';
import { History } from 'history';
import { EMPTY, Observable } from 'rxjs';
import useObservable from 'react-use/lib/useObservable';
@ -55,60 +56,62 @@ export const AppRouter: FunctionComponent<Props> = ({
return (
<Router history={history}>
<Switch>
{[...mounters].map(([appId, mounter]) => (
<CompatRouter>
<Switch>
{[...mounters].map(([appId, mounter]) => (
<Route
key={mounter.appRoute}
path={mounter.appRoute}
exact={mounter.exactRoute}
render={({ match: { path } }) => (
<AppContainer
appPath={path}
appStatus={appStatuses.get(appId) ?? AppStatus.inaccessible}
createScopedHistory={createScopedHistory}
{...{
appId,
mounter,
setAppLeaveHandler,
setAppActionMenu,
setIsMounting,
theme$,
showPlainSpinner,
}}
/>
)}
/>
))}
{/* handler for legacy apps and used as a catch-all to display 404 page on not existing /app/appId apps*/}
<Route
key={mounter.appRoute}
path={mounter.appRoute}
exact={mounter.exactRoute}
render={({ match: { path } }) => (
<AppContainer
appPath={path}
appStatus={appStatuses.get(appId) ?? AppStatus.inaccessible}
createScopedHistory={createScopedHistory}
{...{
appId,
mounter,
setAppLeaveHandler,
setAppActionMenu,
setIsMounting,
theme$,
showPlainSpinner,
}}
/>
)}
path="/app/:appId"
render={({
match: {
params: { appId },
url,
},
}: RouteComponentProps<Params>) => {
// the id/mounter retrieval can be removed once #76348 is addressed
const [id, mounter] = mounters.has(appId) ? [appId, mounters.get(appId)] : [];
return (
<AppContainer
appPath={url}
appId={id ?? appId}
appStatus={appStatuses.get(appId) ?? AppStatus.inaccessible}
createScopedHistory={createScopedHistory}
{...{
mounter,
setAppLeaveHandler,
setAppActionMenu,
setIsMounting,
theme$,
showPlainSpinner,
}}
/>
);
}}
/>
))}
{/* handler for legacy apps and used as a catch-all to display 404 page on not existing /app/appId apps*/}
<Route
path="/app/:appId"
render={({
match: {
params: { appId },
url,
},
}: RouteComponentProps<Params>) => {
// the id/mounter retrieval can be removed once #76348 is addressed
const [id, mounter] = mounters.has(appId) ? [appId, mounters.get(appId)] : [];
return (
<AppContainer
appPath={url}
appId={id ?? appId}
appStatus={appStatuses.get(appId) ?? AppStatus.inaccessible}
createScopedHistory={createScopedHistory}
{...{
mounter,
setAppLeaveHandler,
setAppActionMenu,
setIsMounting,
theme$,
showPlainSpinner,
}}
/>
);
}}
/>
</Switch>
</Switch>
</CompatRouter>
</Router>
);
};

View file

@ -28,8 +28,9 @@ import {
import type { HttpStart } from '@kbn/core-http-browser';
import { MountPoint } from '@kbn/core-mount-utils-browser';
import { i18n } from '@kbn/i18n';
import React, { createRef, useState } from 'react';
import React, { createRef, useCallback, useState } from 'react';
import { Router } from 'react-router-dom';
import { CompatRouter } from 'react-router-dom-v5-compat';
import useLocalStorage from 'react-use/lib/useLocalStorage';
import useObservable from 'react-use/lib/useObservable';
import { Observable, debounceTime } from 'rxjs';
@ -120,12 +121,15 @@ const Logo = (
fullHref = props.prependBasePath(homeHref);
}
const navigateHome = (event: React.MouseEvent) => {
if (fullHref) {
props.application.navigateToUrl(fullHref);
}
event.preventDefault();
};
const navigateHome = useCallback(
(event: React.MouseEvent) => {
if (fullHref) {
props.application.navigateToUrl(fullHref);
}
event.preventDefault();
},
[fullHref, props.application]
);
return (
<span css={logo.container}>
@ -165,36 +169,45 @@ export const ProjectHeader = ({
const toggleCollapsibleNavRef = createRef<HTMLButtonElement & { euiAnimate: () => void }>();
const headerActionMenuMounter = useHeaderActionMenuMounter(observables.actionMenu$);
const handleCloseNav = useCallback(() => {
setIsOpen(false);
if (toggleCollapsibleNavRef.current) {
toggleCollapsibleNavRef.current.focus();
}
}, [setIsOpen, toggleCollapsibleNavRef]);
const handleToggleNavButtonClick = useCallback(
() => setIsOpen((prevIsOpen) => !prevIsOpen),
[setIsOpen]
);
return (
<>
<EuiHeader position="fixed" data-test-subj="kibanaProjectHeader">
<EuiHeaderSection grow={false}>
<EuiHeaderSectionItem css={headerCss.nav.toggleNavButton}>
<Router history={application.history}>
<ProjectNavigation
isOpen={isOpen!}
closeNav={() => {
setIsOpen(false);
if (toggleCollapsibleNavRef.current) {
toggleCollapsibleNavRef.current.focus();
<CompatRouter>
<ProjectNavigation
isOpen={isOpen!}
closeNav={handleCloseNav}
button={
<EuiHeaderSectionItemButton
data-test-subj="toggleNavButton"
aria-label={headerStrings.nav.closeNavAriaLabel}
onClick={handleToggleNavButtonClick}
aria-expanded={isOpen!}
aria-pressed={isOpen!}
aria-controls={navId}
ref={toggleCollapsibleNavRef}
>
<EuiIcon type={isOpen ? 'menuLeft' : 'menuRight'} size="m" />
</EuiHeaderSectionItemButton>
}
}}
button={
<EuiHeaderSectionItemButton
data-test-subj="toggleNavButton"
aria-label={headerStrings.nav.closeNavAriaLabel}
onClick={() => setIsOpen(!isOpen)}
aria-expanded={isOpen!}
aria-pressed={isOpen!}
aria-controls={navId}
ref={toggleCollapsibleNavRef}
>
<EuiIcon type={isOpen ? 'menuLeft' : 'menuRight'} size="m" />
</EuiHeaderSectionItemButton>
}
>
{children}
</ProjectNavigation>
>
{children}
</ProjectNavigation>
</CompatRouter>
</Router>
</EuiHeaderSectionItem>

View file

@ -2,6 +2,7 @@ import React, { useState } from 'react';
import { i18n } from '@kbn/i18n';
import { FormattedMessage, I18nProvider } from '@kbn/i18n-react';
import { BrowserRouter as Router } from 'react-router-dom';
import { CompatRouter } from 'react-router-dom-v5-compat';
import {
EuiButton,
@ -52,62 +53,64 @@ export const <%= upperCamelCase(name) %>App = ({ basename, notifications, http,
// Note that `navigation.ui.TopNavMenu` is a stateful component exported on the `navigation` plugin's start contract.
return (
<Router basename={basename}>
<I18nProvider>
<>
<navigation.ui.TopNavMenu appName={ PLUGIN_ID } showSearchBar={true} useDefaultBehaviors={true}/>
<EuiPage restrictWidth="1000px">
<EuiPageBody>
<EuiPageHeader>
<EuiTitle size="l">
<h1>
<FormattedMessage
id="<%= camelCase(name) %>.helloWorldText"
defaultMessage="{name}"
values={{ name: PLUGIN_NAME }}
/>
</h1>
</EuiTitle>
</EuiPageHeader>
<EuiPageContent>
<EuiPageContentHeader>
<EuiTitle>
<h2>
<CompatRouter>
<I18nProvider>
<>
<navigation.ui.TopNavMenu appName={ PLUGIN_ID } showSearchBar={true} useDefaultBehaviors={true}/>
<EuiPage restrictWidth="1000px">
<EuiPageBody>
<EuiPageHeader>
<EuiTitle size="l">
<h1>
<FormattedMessage
id="<%= camelCase(name) %>.congratulationsTitle"
defaultMessage="Congratulations, you have successfully created a new Kibana Plugin!"
id="<%= camelCase(name) %>.helloWorldText"
defaultMessage="{name}"
values={{ name: PLUGIN_NAME }}
/>
</h2>
</h1>
</EuiTitle>
</EuiPageContentHeader>
<EuiPageContentBody>
<EuiText>
<p>
<FormattedMessage
id="<%= camelCase(name) %>.content"
defaultMessage="Look through the generated code and check out the plugin development documentation."
/>
</p>
<EuiHorizontalRule/>
<p>
<FormattedMessage
id="<%= camelCase(name) %>.timestampText"
defaultMessage="Last timestamp: {time}"
values={{ time: timestamp ? timestamp : 'Unknown' }}
/>
</p>
<EuiButton type="primary" size="s" onClick={onClickHandler}>
<FormattedMessage
id="<%= camelCase(name) %>.buttonText"
defaultMessage="<%= hasServer ? 'Get data' : 'Click me' %>"
/>
</EuiButton>
</EuiText>
</EuiPageContentBody>
</EuiPageContent>
</EuiPageBody>
</EuiPage>
</>
</I18nProvider>
</EuiPageHeader>
<EuiPageContent>
<EuiPageContentHeader>
<EuiTitle>
<h2>
<FormattedMessage
id="<%= camelCase(name) %>.congratulationsTitle"
defaultMessage="Congratulations, you have successfully created a new Kibana Plugin!"
/>
</h2>
</EuiTitle>
</EuiPageContentHeader>
<EuiPageContentBody>
<EuiText>
<p>
<FormattedMessage
id="<%= camelCase(name) %>.content"
defaultMessage="Look through the generated code and check out the plugin development documentation."
/>
</p>
<EuiHorizontalRule/>
<p>
<FormattedMessage
id="<%= camelCase(name) %>.timestampText"
defaultMessage="Last timestamp: {time}"
values={{ time: timestamp ? timestamp : 'Unknown' }}
/>
</p>
<EuiButton type="primary" size="s" onClick={onClickHandler}>
<FormattedMessage
id="<%= camelCase(name) %>.buttonText"
defaultMessage="<%= hasServer ? 'Get data' : 'Click me' %>"
/>
</EuiButton>
</EuiText>
</EuiPageContentBody>
</EuiPageContent>
</EuiPageBody>
</EuiPage>
</>
</I18nProvider>
</CompatRouter>
</Router>
);
};

View file

@ -8,6 +8,7 @@
import { History } from 'history';
import React from 'react';
import { Router as ReactRouter } from 'react-router-dom';
import { CompatRouter } from 'react-router-dom-v5-compat';
import { RouteMap, Router } from './types';
import { RouterContextProvider } from './use_router';
@ -22,7 +23,9 @@ export function RouterProvider({
}) {
return (
<ReactRouter history={history}>
<RouterContextProvider router={router}>{children}</RouterContextProvider>
<CompatRouter>
<RouterContextProvider router={router}>{children}</RouterContextProvider>
</CompatRouter>
</ReactRouter>
);
}

View file

@ -52,6 +52,7 @@ RUNTIME_DEPS = [
"@npm//react-beautiful-dnd",
"@npm//react-dom",
"@npm//react-router-dom",
"@npm//react-router-dom-v5-compat",
"@npm//react-router",
"@npm//react",
"@npm//rxjs",

View file

@ -99,6 +99,7 @@ module.exports = (_, argv) => {
'react-dom',
'react-dom/server',
'react-router-dom',
'react-router-dom-v5-compat',
'react-router',
'react',
'rxjs',

View file

@ -50,6 +50,7 @@ const externals = {
'react-dom/server': '__kbnSharedDeps__.ReactDomServer',
'react-router': '__kbnSharedDeps__.ReactRouter',
'react-router-dom': '__kbnSharedDeps__.ReactRouterDom',
'react-router-dom-v5-compat': '__kbnSharedDeps__.ReactRouterDomV5Compat',
'styled-components': '__kbnSharedDeps__.StyledComponents',
'@kbn/monaco': '__kbnSharedDeps__.KbnMonaco',
// this is how plugins/consumers from npm load monaco

View file

@ -29,6 +29,7 @@ export const ReactDomServer = require('react-dom/server');
// eslint-disable-next-line @kbn/eslint/module_migration
export const ReactRouter = require('react-router');
export const ReactRouterDom = require('react-router-dom');
export const ReactRouterDomV5Compat = require('react-router-dom-v5-compat');
export const StyledComponents = require('styled-components');
Moment.tz.load(require('moment-timezone/data/packed/latest.json'));

View file

@ -23,7 +23,12 @@ import { useSharedUXExecutionContext } from './use_execution_context';
* and send them to the execution context, later used to enrich APM
* 'route-change' transactions.
*/
export const Route = ({ children, component: Component, render, ...rest }: RouteProps) => {
export const Route = ({
children,
component: Component,
render,
...rest
}: RouteProps<string, { [K: string]: string }>) => {
const component = useMemo(() => {
if (!Component) {
return undefined;

View file

@ -9,6 +9,7 @@
import React from 'react';
import ReactDOM from 'react-dom';
import { Router, Switch, Redirect, RouteChildrenProps } from 'react-router-dom';
import { CompatRouter } from 'react-router-dom-v5-compat';
import { Route } from '@kbn/shared-ux-router';
import { i18n } from '@kbn/i18n';
@ -42,10 +43,9 @@ const readOnlyBadge = {
iconType: 'glasses',
};
const redirectUrl = ({
match,
location,
}: RouteChildrenProps<{ [QUERY]: string }>): LocationDescriptor => {
type RedirectUrlProps = RouteChildrenProps<{ [QUERY]: string }>;
const redirectUrl = ({ match, location }: RedirectUrlProps): LocationDescriptor => {
const search = url.addQueryParam(location.search, QUERY, match?.params[QUERY]);
return {
@ -78,26 +78,30 @@ export async function mountManagementSection(
<KibanaThemeProvider theme$={params.theme$}>
<I18nProvider>
<Router history={params.history}>
<Switch>
{/* TODO: remove route param (`query`) in 7.13 */}
<Route path={`/:${QUERY}`}>{(props) => <Redirect to={redirectUrl(props)} />}</Route>
<Route path="/">
<Settings
history={params.history}
enableSaving={{
namespace: canSaveAdvancedSettings,
global: canSaveGlobalSettings,
}}
enableShowing={{ namespace: true, global: canShowGlobalSettings }}
toasts={notifications.toasts}
docLinks={docLinks.links}
settingsService={settings}
theme={params.theme$}
componentRegistry={componentRegistry}
trackUiMetric={trackUiMetric}
/>
</Route>
</Switch>
<CompatRouter>
<Switch>
{/* TODO: remove route param (`query`) in 7.13 */}
<Route path={`/:${QUERY}`}>
{(props: RedirectUrlProps) => <Redirect to={redirectUrl(props)} />}
</Route>
<Route path="/">
<Settings
history={params.history}
enableSaving={{
namespace: canSaveAdvancedSettings,
global: canSaveGlobalSettings,
}}
enableShowing={{ namespace: true, global: canShowGlobalSettings }}
toasts={notifications.toasts}
docLinks={docLinks.links}
settingsService={settings}
theme={params.theme$}
componentRegistry={componentRegistry}
trackUiMetric={trackUiMetric}
/>
</Route>
</Switch>
</CompatRouter>
</Router>
</I18nProvider>
</KibanaThemeProvider>,

View file

@ -12,6 +12,7 @@ import React from 'react';
import { parse, ParsedQuery } from 'query-string';
import { render, unmountComponentAtNode } from 'react-dom';
import { Switch, RouteComponentProps, HashRouter, Redirect } from 'react-router-dom';
import { CompatRouter } from 'react-router-dom-v5-compat';
import { Route } from '@kbn/shared-ux-router';
import { I18nProvider } from '@kbn/i18n-react';
@ -149,17 +150,19 @@ export async function mountApp({ core, element, appUnMounted, mountContext }: Da
<DashboardMountContext.Provider value={mountContext}>
<KibanaThemeProvider theme$={core.theme.theme$}>
<HashRouter>
<Switch>
<Route
path={[CREATE_NEW_DASHBOARD_URL, `${VIEW_DASHBOARD_URL}/:id`]}
render={renderDashboard}
/>
<Route exact path={LANDING_PAGE_PATH} render={renderListingPage} />
<Route exact path="/">
<Redirect to={LANDING_PAGE_PATH} />
</Route>
<Route render={renderNoMatch} />
</Switch>
<CompatRouter>
<Switch>
<Route
path={[CREATE_NEW_DASHBOARD_URL, `${VIEW_DASHBOARD_URL}/:id`]}
render={renderDashboard}
/>
<Route exact path={LANDING_PAGE_PATH} render={renderListingPage} />
<Route exact path="/">
<Redirect to={LANDING_PAGE_PATH} />
</Route>
<Route render={renderNoMatch} />
</Switch>
</CompatRouter>
</HashRouter>
</KibanaThemeProvider>
</DashboardMountContext.Provider>

View file

@ -9,6 +9,7 @@
import React from 'react';
import ReactDOM from 'react-dom';
import { Router, Switch, Redirect } from 'react-router-dom';
import { CompatRouter } from 'react-router-dom-v5-compat';
import { Route } from '@kbn/shared-ux-router';
import { i18n } from '@kbn/i18n';
@ -87,21 +88,23 @@ export async function mountManagementSection(
<KibanaThemeProvider theme$={theme.theme$}>
<I18nProvider>
<Router history={params.history}>
<Switch>
<Route path={['/create']}>
<IndexPatternTableWithRouter canSave={canSave} showCreateDialog={true} />
</Route>
<Route path={['/dataView/:id/field/:fieldName', '/dataView/:id/create-field/']}>
<CreateEditFieldContainer />
</Route>
<Route path={['/dataView/:id']}>
<EditIndexPatternContainer />
</Route>
<Redirect path={'/patterns*'} to={'dataView*'} />
<Route path={['/']}>
<IndexPatternTableWithRouter canSave={canSave} />
</Route>
</Switch>
<CompatRouter>
<Switch>
<Route path={['/create']}>
<IndexPatternTableWithRouter canSave={canSave} showCreateDialog={true} />
</Route>
<Route path={['/dataView/:id/field/:fieldName', '/dataView/:id/create-field/']}>
<CreateEditFieldContainer />
</Route>
<Route path={['/dataView/:id']}>
<EditIndexPatternContainer />
</Route>
<Redirect path={'/patterns*'} to={'dataView*'} />
<Route path={['/']}>
<IndexPatternTableWithRouter canSave={canSave} />
</Route>
</Switch>
</CompatRouter>
</Router>
</I18nProvider>
</KibanaThemeProvider>

View file

@ -10,6 +10,7 @@ import React, { useEffect, useRef } from 'react';
import { Observable } from 'rxjs';
import ReactDOM from 'react-dom';
import { HashRouter as Router, Switch, Redirect, RouteComponentProps } from 'react-router-dom';
import { CompatRouter } from 'react-router-dom-v5-compat';
import { Route } from '@kbn/shared-ux-router';
import { EuiTab, EuiTabs, EuiToolTip, EuiBetaBadge } from '@elastic/eui';
import { I18nProvider } from '@kbn/i18n-react';
@ -183,31 +184,33 @@ export function renderApp(
<I18nProvider>
<KibanaThemeProvider theme$={theme$}>
<Router>
<Switch>
{devTools
// Only create routes for devtools that are not disabled
.filter((devTool) => !devTool.isDisabled())
.map((devTool) => (
<Route
key={devTool.id}
path={`/${devTool.id}`}
exact={!devTool.enableRouting}
render={(props) => (
<DevToolsWrapper
updateRoute={props.history.push}
location={props.location}
activeDevTool={devTool}
devTools={devTools}
theme$={theme$}
appServices={appServices}
/>
)}
/>
))}
<Route path="/">
<Redirect to={`/${devTools[0].id}`} />
</Route>
</Switch>
<CompatRouter>
<Switch>
{devTools
// Only create routes for devtools that are not disabled
.filter((devTool) => !devTool.isDisabled())
.map((devTool) => (
<Route
key={devTool.id}
path={`/${devTool.id}`}
exact={!devTool.enableRouting}
render={(props) => (
<DevToolsWrapper
updateRoute={props.history.push}
location={props.location}
activeDevTool={devTool}
devTools={devTools}
theme$={theme$}
appServices={appServices}
/>
)}
/>
))}
<Route path="/">
<Redirect to={`/${devTools[0].id}`} />
</Route>
</Switch>
</CompatRouter>
</Router>
</KibanaThemeProvider>
</I18nProvider>,

View file

@ -7,6 +7,7 @@
*/
import { Redirect, Router, Switch } from 'react-router-dom';
import { CompatRouter } from 'react-router-dom-v5-compat';
import { Route } from '@kbn/shared-ux-router';
import React from 'react';
import { History } from 'history';
@ -23,30 +24,32 @@ export const discoverRouter = (services: DiscoverServices, history: History, isD
<KibanaContextProvider services={services}>
<EuiErrorBoundary>
<Router history={history} data-test-subj="discover-react-router">
<Switch>
<Route path="/context/:dataViewId/:id">
<ContextAppRoute />
</Route>
<Route
path="/doc/:dataView/:index/:type"
render={(props) => (
<Redirect to={`/doc/${props.match.params.dataView}/${props.match.params.index}`} />
)}
/>
<Route path="/doc/:dataViewId/:index">
<SingleDocRoute />
</Route>
<Route path="/viewAlert/:id">
<ViewAlertRoute />
</Route>
<Route path="/view/:id">
<DiscoverMainRoute isDev={isDev} />
</Route>
<Route path="/" exact>
<DiscoverMainRoute isDev={isDev} />
</Route>
<NotFoundRoute />
</Switch>
<CompatRouter>
<Switch>
<Route path="/context/:dataViewId/:id">
<ContextAppRoute />
</Route>
<Route
path="/doc/:dataView/:index/:type"
render={(props) => (
<Redirect to={`/doc/${props.match.params.dataView}/${props.match.params.index}`} />
)}
/>
<Route path="/doc/:dataViewId/:index">
<SingleDocRoute />
</Route>
<Route path="/viewAlert/:id">
<ViewAlertRoute />
</Route>
<Route path="/view/:id">
<DiscoverMainRoute isDev={isDev} />
</Route>
<Route path="/" exact>
<DiscoverMainRoute isDev={isDev} />
</Route>
<NotFoundRoute />
</Switch>
</CompatRouter>
</Router>
</EuiErrorBoundary>
</KibanaContextProvider>

View file

@ -7,7 +7,7 @@
import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter as Router, RouteComponentProps } from 'react-router-dom';
import { BrowserRouter as Router } from 'react-router-dom';
import { Route } from '@kbn/shared-ux-router';
import { EuiPage } from '@elastic/eui';
import { AppMountParameters, CoreStart } from '@kbn/core/public';
@ -45,7 +45,7 @@ const AlertingExampleApp = ({
/>
<Route
path={`/rule/:id`}
render={(props: RouteComponentProps<{ id: string }>) => {
render={(props) => {
return (
<Page title={`View Rule`} crumb={`View Rule ${props.match.params.id}`}>
<ViewAlertPage http={http} id={props.match.params.id} />
@ -55,7 +55,7 @@ const AlertingExampleApp = ({
/>
<Route
path={`/astros/:id`}
render={(props: RouteComponentProps<{ id: string }>) => {
render={(props) => {
return (
<Page title={`View People In Space Rule`} crumb={`Astros ${props.match.params.id}`}>
<ViewPeopleInSpaceAlertPage http={http} id={props.match.params.id} />

View file

@ -9,7 +9,6 @@ import { act, fireEvent, render } from '@testing-library/react';
import React from 'react';
import { MemoryRouter } from 'react-router-dom';
import { createMemoryHistory } from 'history';
import { RouterProvider } from '@kbn/typed-react-router-config';
import { License } from '@kbn/licensing-plugin/common/license';
import { Transaction } from '../../../../typings/es_schemas/ui/transaction';
import { ApmPluginContextValue } from '../../../context/apm_plugin/apm_plugin_context';
@ -26,7 +25,6 @@ import {
} from '../../../utils/test_helpers';
import { TransactionActionMenu } from './transaction_action_menu';
import * as Transactions from './__fixtures__/mock_data';
import { apmRouter } from '../../routing/apm_route_config';
const apmContextMock = {
...mockApmPluginContextValue,
@ -44,10 +42,8 @@ history.replace(
function Wrapper({ children }: { children?: React.ReactNode }) {
return (
<MemoryRouter>
<MockApmPluginContextWrapper value={apmContextMock}>
<RouterProvider history={history} router={apmRouter as any}>
{children}
</RouterProvider>
<MockApmPluginContextWrapper value={apmContextMock} history={history}>
{children}
</MockApmPluginContextWrapper>
</MemoryRouter>
);

View file

@ -15,7 +15,7 @@ import { decode } from '../route_state';
export const useRestoreHistory = () => {
const history = useHistory();
const location = useLocation();
const location = useLocation<string>();
const dispatch = useDispatch();
const { state: historyState } = location;

View file

@ -14,7 +14,7 @@ import { encode, decode } from '../route_state';
import { State } from '../../../../types';
export const useWorkpadHistory = () => {
const history = useHistory();
const history = useHistory<string>();
const historyState = useSelector((state: State) => state.persistent);
const hasRun = useRef<boolean>(false);

View file

@ -5,6 +5,8 @@
* 2.0.
*/
// eslint-disable-next-line @kbn/eslint/module_migration
import type { ExtractRouteParams } from 'react-router';
import { generatePath } from 'react-router-dom';
import {
CASES_CREATE_PATH,
@ -39,18 +41,24 @@ export const getCaseViewWithCommentPath = (casesBasePath: string) =>
export const generateCaseViewPath = (params: CaseViewPathParams): string => {
const { commentId, tabId } = params;
// Cast for generatePath argument type constraint
const pathParams = params as unknown as { [paramName: string]: string };
// paths with commentId have their own specific path.
// Effectively overwrites the tabId
if (commentId) {
return normalizePath(generatePath(CASE_VIEW_COMMENT_PATH, pathParams));
return normalizePath(
generatePath(
CASE_VIEW_COMMENT_PATH,
params as ExtractRouteParams<typeof CASE_VIEW_COMMENT_PATH>
)
);
}
if (tabId !== undefined) {
return normalizePath(generatePath(CASE_VIEW_TAB_PATH, pathParams));
return normalizePath(
generatePath(CASE_VIEW_TAB_PATH, params as ExtractRouteParams<typeof CASE_VIEW_TAB_PATH>)
);
}
return normalizePath(generatePath(CASE_VIEW_PATH, pathParams));
return normalizePath(
generatePath(CASE_VIEW_PATH, params as ExtractRouteParams<typeof CASE_VIEW_PATH>)
);
};

View file

@ -9,7 +9,6 @@ import type { History } from 'history';
import type { FunctionComponent } from 'react';
import React from 'react';
import { render, unmountComponentAtNode } from 'react-dom';
import type { RouteComponentProps } from 'react-router-dom';
import { Redirect, Router, Switch } from 'react-router-dom';
import type { Observable } from 'rxjs';
@ -37,10 +36,6 @@ interface CreateParams {
getStartServices: StartServicesAccessor<PluginStartDependencies>;
}
interface EditUserParams {
username: string;
}
export const usersManagementApp = Object.freeze({
id: 'users',
create({ authc, getStartServices }: CreateParams) {
@ -109,7 +104,7 @@ export const usersManagementApp = Object.freeze({
</Route>
<Route
path="/edit/:username"
render={(props: RouteComponentProps<EditUserParams>) => {
render={(props) => {
// Additional decoding is a workaround for a bug in react-router's version of the `history` module.
// See https://github.com/elastic/kibana/issues/82440
const username = tryDecodeURIComponent(props.match.params.username);

View file

@ -27,7 +27,7 @@ import {
import { waitForPageFilters } from '../alerts';
export const openFilterGroupContextMenu = () => {
cy.get(DETECTION_PAGE_FILTER_GROUP_CONTEXT_MENU).click({ force: true });
cy.get(DETECTION_PAGE_FILTER_GROUP_CONTEXT_MENU).click();
};
export const waitForFilterGroups = () => {
@ -41,7 +41,7 @@ export const waitForFilterGroups = () => {
export const resetFilterGroup = () => {
openFilterGroupContextMenu();
cy.get(DETECTION_PAGE_FILTER_GROUP_RESET_BUTTON).click({ force: true });
cy.get(DETECTION_PAGE_FILTER_GROUP_RESET_BUTTON).click();
};
export const editFilterGroupControls = () => {
@ -64,11 +64,11 @@ export const saveFilterGroupControls = () => {
export const discardFilterGroupControls = () => {
openFilterGroupContextMenu();
cy.get(FILTER_GROUP_CONTEXT_DISCARD_CHANGES).click({ force: true });
cy.get(FILTER_GROUP_CONTEXT_DISCARD_CHANGES).click();
};
export const openAddFilterGroupControlPanel = () => {
cy.get(FILTER_GROUP_ADD_CONTROL).click({ force: true });
cy.get(FILTER_GROUP_ADD_CONTROL).click();
cy.get(FILTER_GROUP_EDIT_CONTROLS_PANEL).should('be.visible');
};
@ -96,7 +96,7 @@ export const addNewFilterGroupControlValues = ({
export const deleteFilterGroupControl = (idx: number) => {
cy.get(CONTROL_FRAME_TITLE).eq(idx).realHover();
cy.get(FILTER_GROUP_CONTROL_ACTION_DELETE(idx)).click({ force: true });
cy.get(FILTER_GROUP_CONTROL_ACTION_DELETE(idx)).click();
cy.get(FILTER_GROUP_CONTROL_CONFIRM_DIALOG).should('be.visible');
cy.get(FILTER_GROUP_CONTROL_CONFIRM_BTN).click();
};
@ -111,7 +111,7 @@ export const editFilterGroupControl = ({
label: string;
}) => {
cy.get(CONTROL_FRAME_TITLE).eq(idx).realHover();
cy.get(FILTER_GROUP_CONTROL_ACTION_EDIT(idx)).click({ force: true });
cy.get(FILTER_GROUP_CONTROL_ACTION_EDIT(idx)).click();
const { FIELD_SEARCH, FIELD_PICKER, FIELD_LABEL, SAVE } = FILTER_GROUP_EDIT_CONTROL_PANEL_ITEMS;
cy.get(FIELD_SEARCH).type(fieldName);
cy.get(FIELD_PICKER(fieldName)).should('exist').click();

View file

@ -74,7 +74,7 @@ const NetworkContainerComponent = () => {
}) => (
<Redirect
to={{
pathname: getPathWithFlowType(detailName, flowTarget),
pathname: getPathWithFlowType(detailName, flowTarget as FlowTargetSourceDest),
search,
}}
/>

View file

@ -7,6 +7,7 @@
import React, { useState, useEffect } from 'react';
import { Router, Switch, Redirect } from 'react-router-dom';
import { CompatRouter } from 'react-router-dom-v5-compat';
import { Route } from '@kbn/shared-ux-router';
import { FormattedMessage } from '@kbn/i18n-react';
@ -183,7 +184,9 @@ export const App = ({ history }: { history: ScopedHistory }) => {
return (
<Router history={history}>
<AppHandlingClusterUpgradeState />
<CompatRouter>
<AppHandlingClusterUpgradeState />
</CompatRouter>
</Router>
);
};

View file

@ -11,6 +11,7 @@ import { CoreStart } from '@kbn/core/public';
import { of } from 'rxjs';
import { createMemoryHistory } from 'history';
import { Router } from 'react-router-dom';
import { CompatRouter } from 'react-router-dom-v5-compat';
import { MemoryHistory } from 'history';
import { EuiThemeProvider } from '@kbn/kibana-react-plugin/common';
import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public';
@ -38,11 +39,13 @@ export const render = (
return testLibRender(
<Router history={history}>
<KibanaContextProvider services={{ ...core }}>
<UrlParamsProvider>
<EuiThemeProvider>{component}</EuiThemeProvider>
</UrlParamsProvider>
</KibanaContextProvider>
<CompatRouter>
<KibanaContextProvider services={{ ...core }}>
<UrlParamsProvider>
<EuiThemeProvider>{component}</EuiThemeProvider>
</UrlParamsProvider>
</KibanaContextProvider>
</CompatRouter>
</Router>
);
};

View file

@ -11,6 +11,7 @@ import { History, Location } from 'history';
import moment from 'moment-timezone';
import * as React from 'react';
import { MemoryRouter, Router } from 'react-router-dom';
import { CompatRouter } from 'react-router-dom-v5-compat';
import type { UrlParams } from './types';
import { UrlParamsContext, UrlParamsProvider } from './url_params_context';
@ -164,25 +165,27 @@ describe('UrlParamsContext', () => {
const wrapper = mount(
<Router history={history}>
<UrlParamsProvider>
<UrlParamsContext.Consumer>
{({ urlParams, refreshTimeRange }) => {
return (
<React.Fragment>
<span id="data">{JSON.stringify(urlParams, null, 2)}</span>
<button
onClick={() =>
refreshTimeRange({
rangeFrom: 'now-1d/d',
rangeTo: 'now-1d/d',
})
}
/>
</React.Fragment>
);
}}
</UrlParamsContext.Consumer>
</UrlParamsProvider>
<CompatRouter>
<UrlParamsProvider>
<UrlParamsContext.Consumer>
{({ urlParams, refreshTimeRange }) => {
return (
<React.Fragment>
<span id="data">{JSON.stringify(urlParams, null, 2)}</span>
<button
onClick={() =>
refreshTimeRange({
rangeFrom: 'now-1d/d',
rangeTo: 'now-1d/d',
})
}
/>
</React.Fragment>
);
}}
</UrlParamsContext.Consumer>
</UrlParamsProvider>
</CompatRouter>
</Router>
);

View file

@ -18,6 +18,7 @@ import {
import type { SettingsStart } from '@kbn/core-ui-settings-browser';
import { Router, Switch, Redirect, withRouter, RouteComponentProps } from 'react-router-dom';
import { CompatRouter } from 'react-router-dom-v5-compat';
import { Route } from '@kbn/shared-ux-router';
@ -67,11 +68,13 @@ export const App = (deps: AppDeps) => {
}
return (
<Router history={deps.history}>
<ShareRouter>
<AppContextProvider value={deps}>
<AppWithoutRouter />
</AppContextProvider>
</ShareRouter>
<CompatRouter>
<ShareRouter>
<AppContextProvider value={deps}>
<AppWithoutRouter />
</AppContextProvider>
</ShareRouter>
</CompatRouter>
</Router>
);
};

View file

@ -7,7 +7,8 @@
import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter as Router, RouteComponentProps } from 'react-router-dom';
import { BrowserRouter as Router } from 'react-router-dom';
import { CompatRouter } from 'react-router-dom-v5-compat';
import { Route } from '@kbn/shared-ux-router';
import { EuiPage, EuiText } from '@elastic/eui';
@ -21,18 +22,20 @@ const AlertingExampleApp = (deps: AlertingExampleComponentParams) => {
const { basename } = deps;
return (
<Router basename={basename}>
<EuiPage>
<Route
path={`/rule/:id`}
render={(props: RouteComponentProps<{ id: string }>) => {
return (
<EuiText data-test-subj="noop-title">
<h2>View Rule {props.match.params.id}</h2>
</EuiText>
);
}}
/>
</EuiPage>
<CompatRouter>
<EuiPage>
<Route
path={`/rule/:id`}
render={(props) => {
return (
<EuiText data-test-subj="noop-title">
<h2>View Rule {props.match.params.id}</h2>
</EuiText>
);
}}
/>
</EuiPage>
</CompatRouter>
</Router>
);
};

View file

@ -17,6 +17,7 @@ import {
EuiFlexGroup,
} from '@elastic/eui';
import { Router } from 'react-router-dom';
import { CompatRouter } from 'react-router-dom-v5-compat';
import { AppMountParameters, CoreStart } from '@kbn/core/public';
import { CasesUiStart } from '@kbn/cases-plugin/public';
import { CommentType } from '@kbn/cases-plugin/common';
@ -110,9 +111,11 @@ const CasesFixtureApp: React.FC<{ deps: RenderAppProps }> = ({ deps }) => {
>
<StyledComponentsThemeProvider>
<Router history={history}>
<CasesContext owner={[]} permissions={permissions}>
<CasesFixtureAppWithContext cases={cases} />
</CasesContext>
<CompatRouter>
<CasesContext owner={[]} permissions={permissions}>
<CasesFixtureAppWithContext cases={cases} />
</CasesContext>
</CompatRouter>
</Router>
</StyledComponentsThemeProvider>
</KibanaContextProvider>

View file

@ -6,6 +6,7 @@
*/
import { Router } from 'react-router-dom';
import { CompatRouter } from 'react-router-dom-v5-compat';
import React from 'react';
import ReactDOM from 'react-dom';
@ -77,19 +78,21 @@ const AppRoot = React.memo(
return (
<I18nProvider>
<Router history={parameters.history}>
<KibanaContextProvider services={coreStart}>
<Provider store={store}>
<Wrapper>
<ResolverWithoutProviders
databaseDocumentID=""
resolverComponentInstanceID="test"
indices={[]}
shouldUpdate={false}
filters={{}}
/>
</Wrapper>
</Provider>
</KibanaContextProvider>
<CompatRouter>
<KibanaContextProvider services={coreStart}>
<Provider store={store}>
<Wrapper>
<ResolverWithoutProviders
databaseDocumentID=""
resolverComponentInstanceID="test"
indices={[]}
shouldUpdate={false}
filters={{}}
/>
</Wrapper>
</Provider>
</KibanaContextProvider>
</CompatRouter>
</Router>
</I18nProvider>
);

113
yarn.lock
View file

@ -1185,7 +1185,7 @@
core-js-pure "^3.25.1"
regenerator-runtime "^0.13.10"
"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.10.2", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.0", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.13", "@babel/runtime@^7.12.5", "@babel/runtime@^7.15.4", "@babel/runtime@^7.17.8", "@babel/runtime@^7.18.3", "@babel/runtime@^7.21.0", "@babel/runtime@^7.3.1", "@babel/runtime@^7.4.4", "@babel/runtime@^7.4.5", "@babel/runtime@^7.5.0", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.3", "@babel/runtime@^7.7.2", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2":
"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.10.2", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.0", "@babel/runtime@^7.12.13", "@babel/runtime@^7.12.5", "@babel/runtime@^7.15.4", "@babel/runtime@^7.17.8", "@babel/runtime@^7.18.3", "@babel/runtime@^7.21.0", "@babel/runtime@^7.3.1", "@babel/runtime@^7.4.4", "@babel/runtime@^7.4.5", "@babel/runtime@^7.5.0", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.3", "@babel/runtime@^7.7.2", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2":
version "7.21.0"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.21.0.tgz#5b55c9d394e5fcf304909a8b00c07dc217b56673"
integrity sha512-xwII0//EObnq89Ji5AKYQaRYiW/nZ3llSv29d49IuxPhKbtJoLP+9QUUZ4nVragQVtaVGeZrpB+ZtG/Pdy/POw==
@ -6779,6 +6779,11 @@
redux-thunk "^2.4.1"
reselect "^4.1.5"
"@remix-run/router@1.6.3":
version "1.6.3"
resolved "https://registry.yarnpkg.com/@remix-run/router/-/router-1.6.3.tgz#8205baf6e17ef93be35bf62c37d2d594e9be0dad"
integrity sha512-EXJysQ7J3veRECd0kZFQwYYd5sJMcq2O/m60zu1W2l3oVQ9xtub8jTOtYRE0+M2iomyG/W3Ps7+vp2kna0C27Q==
"@samverschueren/stream-to-observable@^0.3.0":
version "0.3.0"
resolved "https://registry.yarnpkg.com/@samverschueren/stream-to-observable/-/stream-to-observable-0.3.0.tgz#ecdf48d532c58ea477acfcab80348424f8d0662f"
@ -8606,15 +8611,10 @@
resolved "https://registry.yarnpkg.com/@types/he/-/he-1.1.1.tgz#19e14033c4ee8f1a702c74dcc6182664839ac2b7"
integrity sha512-jpzrsR1ns0n3kyWt92QfOUQhIuJGQ9+QGa7M62rO6toe98woQjnsnzjdMtsQXCdvjjmqjS2ZBCC7xKw0cdzU+Q==
"@types/history@*":
version "4.7.3"
resolved "https://registry.yarnpkg.com/@types/history/-/history-4.7.3.tgz#856c99cdc1551d22c22b18b5402719affec9839a"
integrity sha512-cS5owqtwzLN5kY+l+KgKdRJ/Cee8tlmQoGQuIE9tWnSmS3JMKzmxo2HIAk2wODMifGwO20d62xZQLYz+RLfXmw==
"@types/history@^4.7.9":
version "4.7.9"
resolved "https://registry.yarnpkg.com/@types/history/-/history-4.7.9.tgz#1cfb6d60ef3822c589f18e70f8b12f9a28ce8724"
integrity sha512-MUc6zSmU3tEVnkQ78q0peeEjKWPUADMlC/t++2bI8WnAG2tvYRPIgHG8lWkXwqc8MsUF6Z2MOf+Mh5sazOmhiQ==
"@types/history@^4.7.9", "@types/history@^4.7.11":
version "4.7.11"
resolved "https://registry.yarnpkg.com/@types/history/-/history-4.7.11.tgz#56588b17ae8f50c53983a524fc3cc47437969d64"
integrity sha512-qjDJRrmvBMiTx+jyLxvLfJU7UznFuokDv4f3WRuriHKERccVpFU+8XMQUAbDzoiJCsmexxRExQeMwwCdamSKDA==
"@types/hjson@^2.4.2":
version "2.4.2"
@ -9216,30 +9216,30 @@
hoist-non-react-statics "^3.3.0"
redux "^4.0.0"
"@types/react-router-config@^5.0.2":
version "5.0.2"
resolved "https://registry.yarnpkg.com/@types/react-router-config/-/react-router-config-5.0.2.tgz#4d3b52e71ed363a1976a12321e67b09a99ad6d10"
integrity sha512-WOSetDV3YPxbkVJAdv/bqExJjmcdCi/vpCJh3NfQOy1X15vHMSiMioXIcGekXDJJYhqGUMDo9e337mh508foAA==
"@types/react-router-config@^5.0.7":
version "5.0.7"
resolved "https://registry.yarnpkg.com/@types/react-router-config/-/react-router-config-5.0.7.tgz#36207a3fe08b271abee62b26993ee932d13cbb02"
integrity sha512-pFFVXUIydHlcJP6wJm7sDii5mD/bCmmAY0wQzq+M+uX7bqS95AQqHZWP1iNMKrWVQSuHIzj5qi9BvrtLX2/T4w==
dependencies:
"@types/history" "*"
"@types/history" "^4.7.11"
"@types/react" "*"
"@types/react-router" "^5.1.0"
"@types/react-router-dom@^5.3.3":
version "5.3.3"
resolved "https://registry.yarnpkg.com/@types/react-router-dom/-/react-router-dom-5.3.3.tgz#e9d6b4a66fcdbd651a5f106c2656a30088cc1e83"
integrity sha512-kpqnYK4wcdm5UaWI3fLcELopqLrHgLqNsdpHauzlQktfkHL3npOSwtj1Uz9oKBAzs7lFtVkV8j83voAz2D8fhw==
dependencies:
"@types/history" "^4.7.11"
"@types/react" "*"
"@types/react-router" "*"
"@types/react-router-dom@^5.1.5":
version "5.1.5"
resolved "https://registry.yarnpkg.com/@types/react-router-dom/-/react-router-dom-5.1.5.tgz#7c334a2ea785dbad2b2dcdd83d2cf3d9973da090"
integrity sha512-ArBM4B1g3BWLGbaGvwBGO75GNFbLDUthrDojV2vHLih/Tq8M+tgvY1DSwkuNrPSwdp/GUL93WSEpTZs8nVyJLw==
"@types/react-router@*", "@types/react-router@^5.1.0", "@types/react-router@^5.1.20":
version "5.1.20"
resolved "https://registry.yarnpkg.com/@types/react-router/-/react-router-5.1.20.tgz#88eccaa122a82405ef3efbcaaa5dcdd9f021387c"
integrity sha512-jGjmu/ZqS7FjSH6owMcD5qpq19+1RS9DeVRqfl1FeBMxTDQAGwlMWOcs52NDoXaNKyG3d1cYQFMs9rCrb88o9Q==
dependencies:
"@types/history" "*"
"@types/react" "*"
"@types/react-router" "*"
"@types/react-router@*", "@types/react-router@^5.1.7":
version "5.1.7"
resolved "https://registry.yarnpkg.com/@types/react-router/-/react-router-5.1.7.tgz#e9d12ed7dcfc79187e4d36667745b69a5aa11556"
integrity sha512-2ouP76VQafKjtuc0ShpwUebhHwJo0G6rhahW9Pb8au3tQTjYXd2jta4wv6U2tGLR/I42yuG00+UXjNYY0dTzbg==
dependencies:
"@types/history" "*"
"@types/history" "^4.7.11"
"@types/react" "*"
"@types/react-syntax-highlighter@^15.4.0":
@ -17528,6 +17528,13 @@ history@^4.9.0:
tiny-warning "^1.0.0"
value-equal "^0.4.0"
history@^5.3.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"
hjson@3.2.1:
version "3.2.1"
resolved "https://registry.yarnpkg.com/hjson/-/hjson-3.2.1.tgz#20de41dc87fc9a10d1557d0230b0e02afb1b09ac"
@ -21401,14 +21408,6 @@ min-indent@^1.0.0:
resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.0.tgz#cfc45c37e9ec0d8f0a0ec3dd4ef7f7c3abe39256"
integrity sha1-z8RcN+nsDY8KDsPdTvf3w6vjklY=
mini-create-react-context@^0.4.0:
version "0.4.1"
resolved "https://registry.yarnpkg.com/mini-create-react-context/-/mini-create-react-context-0.4.1.tgz#072171561bfdc922da08a60c2197a497cc2d1d5e"
integrity sha512-YWCYEmd5CQeHGSAKrYvXgmzzkrvssZcuuQDDeqkT+PziKGMgE+0MCCtcKbROzocGBG1meBLl2FotlRwf4gAzbQ==
dependencies:
"@babel/runtime" "^7.12.1"
tiny-warning "^1.0.3"
mini-css-extract-plugin@1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-1.1.0.tgz#dcc2f0bfbec660c0bd1200ff7c8f82deec2cc8a6"
@ -24689,35 +24688,49 @@ react-router-config@^5.1.1:
dependencies:
"@babel/runtime" "^7.1.2"
react-router-dom@^5.2.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-5.2.0.tgz#9e65a4d0c45e13289e66c7b17c7e175d0ea15662"
integrity sha512-gxAmfylo2QUjcwxI63RhQ5G85Qqt4voZpUXSEqCwykV0baaOTQDR1f0PmY8AELqIyVc0NEZUj0Gov5lNGcXgsA==
react-router-dom-v5-compat@^6.12.0:
version "6.12.0"
resolved "https://registry.yarnpkg.com/react-router-dom-v5-compat/-/react-router-dom-v5-compat-6.12.0.tgz#d5b1df5dafce6120626e568fb3d827353941c026"
integrity sha512-ewEx9QaGOPIEGhd7Y43xLBscqab1Et2LjAoCZo65jRB/naJypZpsqqJfzXRZax0PLNHebl/nACDvp3abxx565Q==
dependencies:
"@babel/runtime" "^7.1.2"
history "^5.3.0"
react-router "6.12.0"
react-router-dom@^5.3.4:
version "5.3.4"
resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-5.3.4.tgz#2ed62ffd88cae6db134445f4a0c0ae8b91d2e5e6"
integrity sha512-m4EqFMHv/Ih4kpcBCONHbkT68KoAeHN4p3lAGoNryfHi0dMy0kCzEZakiKRsvg5wHZ/JLrLW8o8KomWiz/qbYQ==
dependencies:
"@babel/runtime" "^7.12.13"
history "^4.9.0"
loose-envify "^1.3.1"
prop-types "^15.6.2"
react-router "5.2.0"
react-router "5.3.4"
tiny-invariant "^1.0.2"
tiny-warning "^1.0.0"
react-router@5.2.0, react-router@^5.2.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/react-router/-/react-router-5.2.0.tgz#424e75641ca8747fbf76e5ecca69781aa37ea293"
integrity sha512-smz1DUuFHRKdcJC0jobGo8cVbhO3x50tCL4icacOlcwDOEQPq4TMqwx3sY1TP+DvtTgz4nm3thuo7A+BK2U0Dw==
react-router@5.3.4, react-router@^5.3.4:
version "5.3.4"
resolved "https://registry.yarnpkg.com/react-router/-/react-router-5.3.4.tgz#8ca252d70fcc37841e31473c7a151cf777887bb5"
integrity sha512-Ys9K+ppnJah3QuaRiLxk+jDWOR1MekYQrlytiXxC1RyfbdsZkS5pvKAzCCr031xHixZwpnsYNT5xysdFHQaYsA==
dependencies:
"@babel/runtime" "^7.1.2"
"@babel/runtime" "^7.12.13"
history "^4.9.0"
hoist-non-react-statics "^3.1.0"
loose-envify "^1.3.1"
mini-create-react-context "^0.4.0"
path-to-regexp "^1.7.0"
prop-types "^15.6.2"
react-is "^16.6.0"
tiny-invariant "^1.0.2"
tiny-warning "^1.0.0"
react-router@6.12.0:
version "6.12.0"
resolved "https://registry.yarnpkg.com/react-router/-/react-router-6.12.0.tgz#1afae9219c24c8611809469d7a386c8023ade39a"
integrity sha512-/tCGtLq9umxRvbYeIx3j94CmpQfue0E3qnetVm9luKhu58cR4t+3O4ZrQXBdXfJrBATOAj+wF/1ihJJQI8AoTw==
dependencies:
"@remix-run/router" "1.6.3"
react-select@^3.2.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/react-select/-/react-select-3.2.0.tgz#de9284700196f5f9b5277c5d850a9ce85f5c72fe"
@ -27752,7 +27765,7 @@ tiny-invariant@^1.0.2, tiny-invariant@^1.0.6:
resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.0.6.tgz#b3f9b38835e36a41c843a3b0907a5a7b3755de73"
integrity sha512-FOyLWWVjG+aC0UqG76V53yAWdXfH8bO6FNmyZOuUrzDzK8DI3/JRY25UD7+g49JWM1LXwymsKERB+DzI0dTEQA==
tiny-warning@^1.0.0, tiny-warning@^1.0.2, tiny-warning@^1.0.3:
tiny-warning@^1.0.0, tiny-warning@^1.0.2:
version "1.0.3"
resolved "https://registry.yarnpkg.com/tiny-warning/-/tiny-warning-1.0.3.tgz#94a30db453df4c643d0fd566060d60a875d84754"
integrity sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==