diff --git a/CHANGELOG.md b/CHANGELOG.md index 8ca59179..fd1a4d36 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,11 @@ +# 6.4.3 + +### Bug Fixes + +- [#213](https://github.com/okta/okta-react/pull/213) + - Avoids build time error when `react-router-dom` v6 is in app dependencies + - Throws unsupported error when `SecureRoute` is used with `react-router-dom` v6 + # 6.4.2 ### Bug Fixes diff --git a/package.json b/package.json index 77cc7a38..bb1e9c2b 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,6 @@ }, "peerDependencies": { "@okta/okta-auth-js": "^5.3.1 || ^6.0.0", - "@types/react-router-dom": "^5.1.6", "react": ">=16.8.0", "react-dom": ">=16.8.0", "react-router-dom": ">=5.1.0" diff --git a/src/SecureRoute.tsx b/src/SecureRoute.tsx index 301b1439..0d114fb4 100644 --- a/src/SecureRoute.tsx +++ b/src/SecureRoute.tsx @@ -13,9 +13,22 @@ import * as React from 'react'; import { useOktaAuth, OnAuthRequiredFunction } from './OktaContext'; import * as ReactRouterDom from 'react-router-dom'; -import { toRelativeUrl } from '@okta/okta-auth-js'; +import { toRelativeUrl, AuthSdkError } from '@okta/okta-auth-js'; import OktaError from './OktaError'; +// eslint-disable-next-line @typescript-eslint/no-explicit-any +let useMatch: any; +if ('useRouteMatch' in ReactRouterDom) { + // trick static analyzer to avoid "'useRouteMatch' is not exported" error + // eslint-disable-next-line @typescript-eslint/no-explicit-any + useMatch = (ReactRouterDom as any)['useRouteMatch' in ReactRouterDom ? 'useRouteMatch' : '']; +} else { + // throw when useMatch is triggered + useMatch = () => { + throw new AuthSdkError('Unsupported: SecureRoute only works with react-router-dom v5 or any router library with compatible APIs. See examples under the "samples" folder for how to implement your own custom SecureRoute Component.'); + }; +} + const SecureRoute: React.FC<{ onAuthRequired?: OnAuthRequiredFunction; errorComponent?: React.ComponentType<{ error: Error }>; @@ -25,7 +38,7 @@ const SecureRoute: React.FC<{ ...routeProps }) => { const { oktaAuth, authState, _onAuthRequired } = useOktaAuth(); - const match = ReactRouterDom.useRouteMatch(routeProps); + const match = useMatch(routeProps); const pendingLogin = React.useRef(false); const [handleLoginError, setHandleLoginError] = React.useState(null); const ErrorReporter = errorComponent || OktaError; diff --git a/test/jest/reactRouterV6.test.tsx b/test/jest/reactRouterV6.test.tsx new file mode 100644 index 00000000..aa11a540 --- /dev/null +++ b/test/jest/reactRouterV6.test.tsx @@ -0,0 +1,88 @@ +/*! + * Copyright (c) 2017-Present, Okta, Inc. and/or its affiliates. All rights reserved. + * The Okta software accompanied by this notice is provided pursuant to the Apache License, Version 2.0 (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 * as React from 'react'; +import { act } from 'react-dom/test-utils'; +import { render } from 'react-dom'; +import SecureRoute from '../../src/SecureRoute'; +import OktaContext from '../../src/OktaContext'; +import { AuthSdkError } from '@okta/okta-auth-js'; + +jest.mock('react-router-dom', () => ({ + __esModule: true, + useMatch: jest.fn() +})); + +class ErrorBoundary extends React.Component { + constructor(props: any) { + super(props); + this.state = { + error: null + } as { + error: AuthSdkError | null + }; + } + + componentDidCatch(error: AuthSdkError) { + this.setState({ error: error }); + } + + render() { + if (this.state.error) { + // You can render any custom fallback UI + return

{ this.state.error.toString() }

; + } + + return this.props.children; + } +} + +describe('react-router-dom v6', () => { + let oktaAuth: any; + let authState: any; + + beforeEach(() => { + authState = null; + oktaAuth = { + options: {}, + authStateManager: { + getAuthState: jest.fn().mockImplementation(() => authState), + subscribe: jest.fn(), + unsubscribe: jest.fn(), + updateAuthState: jest.fn(), + }, + isLoginRedirect: jest.fn().mockImplementation(() => false), + handleLoginRedirect: jest.fn(), + signInWithRedirect: jest.fn(), + setOriginalUri: jest.fn(), + start: jest.fn(), + }; + }); + + it('throws unsupported error', async () => { + const container = document.createElement('div'); + await act(async () => { + render( + + + + + , + container + ); + }); + expect(container.innerHTML).toBe('

AuthSdkError: Unsupported: SecureRoute only works with react-router-dom v5 or any router library with compatible APIs. See examples under the "samples" folder for how to implement your own custom SecureRoute Component.

'); + }) +}); \ No newline at end of file