Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat!: major version upgrade of react-router-version to v6 #422

Merged
merged 9 commits into from
Aug 8, 2023
25 changes: 14 additions & 11 deletions example/index.jsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
import 'core-js/stable';
import 'regenerator-runtime/runtime';

import { Routes, Route } from 'react-router-dom';
import React from 'react';
import ReactDOM from 'react-dom';
import { APP_INIT_ERROR, APP_READY, initialize } from '@edx/frontend-platform';
import { subscribe } from '@edx/frontend-platform/pubSub';

import {
AppProvider,
AuthenticatedPageRoute,
ErrorPage,
PageRoute,
} from '@edx/frontend-platform/react';
import { APP_INIT_ERROR, APP_READY, initialize } from '@edx/frontend-platform';
import { subscribe } from '@edx/frontend-platform/pubSub';
PageWrap,
} from '../src/react';
Syed-Ali-Abbas-Zaidi marked this conversation as resolved.
Show resolved Hide resolved

import './index.scss';
import ExamplePage from './ExamplePage';
Expand All @@ -19,13 +21,14 @@ import AuthenticatedPage from './AuthenticatedPage';
subscribe(APP_READY, () => {
ReactDOM.render(
<AppProvider>
<PageRoute exact path="/" component={ExamplePage} />
<PageRoute
exact
path="/error_example"
component={() => <ErrorPage message="Test error message" />}
/>
<AuthenticatedPageRoute exact path="/authenticated" component={AuthenticatedPage} />
<Routes>
<Route path="/" element={<PageWrap><ExamplePage /></PageWrap>} />
<Route
path="/error_example"
element={<PageWrap><ErrorPage message="Test error message" /></PageWrap>}
/>
<Route path="/authenticated" element={<AuthenticatedPageRoute><AuthenticatedPage /></AuthenticatedPageRoute>} />
</Routes>
</AppProvider>,
document.getElementById('root'),
);
Expand Down
64 changes: 29 additions & 35 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
"react": "16.14.0",
"react-dom": "16.14.0",
"react-redux": "7.2.9",
"react-router-dom": "5.3.4",
"react-router-dom": "^6.6.1",
"redux": "4.2.1",
"regenerator-runtime": "0.13.11"
},
Expand Down Expand Up @@ -79,7 +79,7 @@
"react": "^16.9.0 || ^17.0.0",
"react-dom": "^16.9.0 || ^17.0.0",
"react-redux": "^7.1.1",
"react-router-dom": "^5.0.1",
"react-router-dom": "^6.0.0",
"redux": "^4.0.4"
}
}
10 changes: 5 additions & 5 deletions src/initialize.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,19 @@
* APP_READY,
* subscribe,
* } from '@edx/frontend-platform';
* import { AppProvider, ErrorPage, PageRoute } from '@edx/frontend-platform/react';
* import { AppProvider, ErrorPage, PageWrap } from '@edx/frontend-platform/react';
* import React from 'react';
* import ReactDOM from 'react-dom';
* import { Switch } from 'react-router-dom';
* import { Routes, Route } from 'react-router-dom';
*
* subscribe(APP_READY, () => {
* ReactDOM.render(
* <AppProvider store={configureStore()}>
* <Header />
* <main>
* <Switch>
* <PageRoute exact path="/" component={PaymentPage} />
* </Switch>
* <Routes>
* <Route path="/" element={<PageWrap><PaymentPage /></PageWrap>} />
* </Routes>
* </main>
* <Footer />
* </AppProvider>,
Expand Down
15 changes: 9 additions & 6 deletions src/react/AppProvider.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React, { useState, useMemo } from 'react';
import PropTypes from 'prop-types';
import { Router } from 'react-router-dom';
import { BrowserRouter as Router } from 'react-router-dom';

import OptionalReduxProvider from './OptionalReduxProvider';

Expand All @@ -10,7 +10,6 @@ import { useAppEvent, useTrackColorSchemeChoice } from './hooks';
import { getAuthenticatedUser, AUTHENTICATED_USER_CHANGED } from '../auth';
import { getConfig } from '../config';
import { CONFIG_CHANGED } from '../constants';
import { history } from '../initialize';
import {
getLocale,
getMessages,
Expand Down Expand Up @@ -44,7 +43,7 @@ import {
* @param {Object} [props.store] A redux store.
* @memberof module:React
*/
export default function AppProvider({ store, children }) {
export default function AppProvider({ store, children, wrapWithRouter }) {
const [config, setConfig] = useState(getConfig());
const [authenticatedUser, setAuthenticatedUser] = useState(getAuthenticatedUser());
const [locale, setLocale] = useState(getLocale());
Expand Down Expand Up @@ -72,9 +71,11 @@ export default function AppProvider({ store, children }) {
value={appContextValue}
>
<OptionalReduxProvider store={store}>
<Router history={history}>
{children}
</Router>
{wrapWithRouter ? (
<Router>
{children}
</Router>
) : children}
</OptionalReduxProvider>
</AppContext.Provider>
</ErrorBoundary>
Expand All @@ -86,8 +87,10 @@ AppProvider.propTypes = {
// eslint-disable-next-line react/forbid-prop-types
store: PropTypes.object,
children: PropTypes.node.isRequired,
wrapWithRouter: PropTypes.bool,
};

AppProvider.defaultProps = {
store: null,
wrapWithRouter: true,
};
23 changes: 22 additions & 1 deletion src/react/AppProvider.test.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React from 'react';
import { createStore } from 'redux';
import { mount } from 'enzyme';
import { BrowserRouter as Router } from 'react-router-dom';
import AppProvider from './AppProvider';
import { initialize } from '../initialize';

Expand Down Expand Up @@ -48,7 +49,7 @@ describe('AppProvider', () => {
});
});

it('should render its children', () => {
it('should render its children with a router', () => {
const component = (
<AppProvider store={createStore(state => state)}>
<div>Child One</div>
Expand All @@ -58,6 +59,26 @@ describe('AppProvider', () => {

const wrapper = mount(component);
const list = wrapper.find('div');
expect(wrapper.find(Router).length).toEqual(1);
expect(list.length).toEqual(2);
expect(list.at(0).text()).toEqual('Child One');
expect(list.at(1).text()).toEqual('Child Two');

const reduxProvider = wrapper.find('Provider');
expect(reduxProvider.length).toEqual(1);
});

it('should render its children without a router', () => {
const component = (
<AppProvider store={createStore(state => state)} wrapWithRouter={false}>
<div>Child One</div>
<div>Child Two</div>
</AppProvider>
);

const wrapper = mount(component);
const list = wrapper.find('div');
expect(wrapper.find(Router).length).toEqual(0);
expect(list.length).toEqual(2);
expect(list.at(0).text()).toEqual('Child One');
expect(list.at(1).text()).toEqual('Child Two');
Expand Down
36 changes: 12 additions & 24 deletions src/react/AuthenticatedPageRoute.jsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import React, { useContext } from 'react';
import PropTypes from 'prop-types';
import { useRouteMatch } from 'react-router-dom';

import AppContext from './AppContext';
import PageRoute from './PageRoute';
import PageWrap from './PageWrap';
import { getLoginRedirectUrl } from '../auth';

/**
Expand All @@ -14,45 +13,34 @@ import { getLoginRedirectUrl } from '../auth';
*
* It can optionally accept an override URL to redirect to instead of the login page.
*
* Like a `PageRoute`, also calls `sendPageEvent` when the route becomes active.
* Like a `PageWrap`, also calls `sendPageEvent` when the route becomes active.
*
* @see PageRoute
* @see PageWrap
* @see {@link module:frontend-platform/analytics~sendPageEvent}
* @memberof module:React
* @param {Object} props
* @param {string} props.redirectUrl The URL anonymous users should be redirected to, rather than
* viewing the route's contents.
*/
export default function AuthenticatedPageRoute({ redirectUrl, ...props }) {
export default function AuthenticatedPageRoute({ redirectUrl, children }) {
const { authenticatedUser } = useContext(AppContext);

const match = useRouteMatch({
// eslint-disable-next-line react/prop-types
path: props.path,
// eslint-disable-next-line react/prop-types
exact: props.exact,
// eslint-disable-next-line react/prop-types
strict: props.strict,
// eslint-disable-next-line react/prop-types
sensitive: props.sensitive,
});

if (authenticatedUser === null) {
if (match) {
const destination = redirectUrl || getLoginRedirectUrl(global.location.href);
global.location.assign(destination);
}
// This emulates a Route's way of displaying nothing if the route's path doesn't match the
// current URL.
const destination = redirectUrl || getLoginRedirectUrl(global.location.href);
global.location.assign(destination);

return null;
}

return (
<PageRoute {...props} />
<PageWrap>
{children}
</PageWrap>
);
}

AuthenticatedPageRoute.propTypes = {
redirectUrl: PropTypes.string,
children: PropTypes.node.isRequired,
};

AuthenticatedPageRoute.defaultProps = {
Expand Down
Loading