[shared-ux-router] Add Router and Routes components (#159834)

## Summary

Why?

To simplify the process of migration to react-router@6.
https://github.com/remix-run/react-router/discussions/8753

What problems exactly it solves?

- In my previous PR I added `CompatRouter`
https://github.com/elastic/kibana/pull/159173, which caused changes in
~50 files and pinged 15 Teams. And this is just meant to be a temporary
change, so when we're done with the migration I would have to revert
these changes and engage everyone to review the PR again. And it is just
a single step in the migration strategy. So to make our lives easier I
think it would be better to have a common place where we do import our
router components because it will allow us to surface some extra logic
in single place instead of going through the whole source code again.

- `react-router@6` doesn't support a custom `Route` component, so that
means our custom `Route` component that we're using almost everywhere
today, will need to be replaced by a different solution. I have decided
to add `Routes` component, which will be responsible for rendering the
proper component (`react-router@6` renamed `Switch` to `Routes`, so I
have named this component to align with the dictionary of the new
router) and also is going to add the logic that today is done in `Route`
(moving logic to `Routes` will be done in the follow-up PR, here I just
wanted to focus on using the common router components to make the review
process easier)

---------

Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Patryk Kopyciński 2023-06-23 17:02:06 +02:00 committed by GitHub
parent bb3aa54c86
commit a1d02824f1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
274 changed files with 2130 additions and 2506 deletions

View file

@ -7,9 +7,8 @@
*/
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 { Link, useLocation } from 'react-router-dom';
import { Router, Routes, Route } from '@kbn/shared-ux-router';
import { History } from 'history';
import {
EuiButton,
@ -186,59 +185,57 @@ export const TodoAppPage: React.FC<{
return (
<Router history={props.history}>
<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>
<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>
<Routes>
<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>
</Routes>
<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>
</Router>
);
};

View file

@ -8,8 +8,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 { Router } from '@kbn/shared-ux-router';
import {
EuiFieldText,
@ -80,42 +79,38 @@ export const App = ({
return (
<StateContainersExamplesPage navigateToApp={navigateToApp} exampleLinks={exampleLinks}>
<Router history={history}>
<CompatRouter>
<EuiPageBody>
<EuiPageHeader>
<EuiTitle size="l">
<h1>Integration with search bar</h1>
</EuiTitle>
</EuiPageHeader>
<EuiText>
<p>
This examples shows how you can use state containers, state syncing utils and
helpers from data plugin to sync your app state and search bar state with the URL.
</p>
</EuiText>
<EuiPageBody>
<EuiPageHeader>
<EuiTitle size="l">
<h1>Integration with search bar</h1>
</EuiTitle>
</EuiPageHeader>
<EuiText>
<p>
This examples shows how you can use state containers, state syncing utils and helpers
from data plugin to sync your app state and search bar state with the URL.
</p>
</EuiText>
<navigation.ui.TopNavMenu
appName={'Example'}
showSearchBar={true}
indexPatterns={[dataView]}
useDefaultBehaviors={true}
showSaveQuery={true}
<navigation.ui.TopNavMenu
appName={'Example'}
showSearchBar={true}
indexPatterns={[dataView]}
useDefaultBehaviors={true}
showSaveQuery={true}
/>
<EuiPageContent>
<EuiText>
<p>In addition to state from query bar also sync your arbitrary application state:</p>
</EuiText>
<EuiFieldText
placeholder="Additional example applications state: My name is..."
value={appState.name}
onChange={(e) => appStateContainer.set({ ...appState, name: e.target.value })}
aria-label="My name"
/>
<EuiPageContent>
<EuiText>
<p>
In addition to state from query bar also sync your arbitrary application state:
</p>
</EuiText>
<EuiFieldText
placeholder="Additional example applications state: My name is..."
value={appState.name}
onChange={(e) => appStateContainer.set({ ...appState, name: e.target.value })}
aria-label="My name"
/>
</EuiPageContent>
</EuiPageBody>
</CompatRouter>
</EuiPageContent>
</EuiPageBody>
</Router>
</StateContainersExamplesPage>
);