Skip to content

Commit 8520585

Browse files
chore: upgrade to react router v6 (PROVCON-3003) (#2757)
* chore: upgrade to react router v6 (PROVCON-3003) * chore: fix build (PROVCON-3003) * chore: fix react-cra (PROVCON-3003) * chore: fix rendering elements in context of external router (PROVCON-3003) * chore: revert retrieving search params (PROVCON-3003) * chore: remove legacy provider flag (PROVCON-3003) * chore: fix angular tests (PROVCON-3003) * chore: bump versions (PROVCON-3003) * chore: correct test and revert versions (PROVCON-3003) * chore: bump up versions (PROVCON-3003) * chore: bump up only elements-core version (PROVCON-3003) * chore: bump up all versions (PROVCON-3003)
1 parent be11c9a commit 8520585

File tree

35 files changed

+4029
-4500
lines changed

35 files changed

+4029
-4500
lines changed

demo/.babelrc

+2-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
"module-resolver",
66
{
77
"alias": {
8-
"@stoplight/elements": "../packages/elements/src"
8+
"@stoplight/elements": "../packages/elements/src",
9+
"@stoplight/elements-dev-portal": "../packages/elements-dev-portal/src"
910
}
1011
}
1112
],

demo/package.json

+3-2
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,13 @@
1111
},
1212
"dependencies": {
1313
"@stoplight/mosaic": "^1.53.4",
14-
"@stoplight/elements": "^8.4.2",
14+
"@stoplight/elements": "^9.0.0",
15+
"@stoplight/elements-dev-portal": "^3.0.0",
1516
"history": "^5.0.0",
1617
"react": "16.14.0",
1718
"react-dom": "16.14.0",
1819
"react-github-btn": "1.4.0",
19-
"react-router-dom": "6.0.0-beta.0"
20+
"react-router-dom": "^6.28.0"
2021
},
2122
"devDependencies": {
2223
"@types/jest": "^26.0.22",

demo/src/components/ElementsAPI.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ export const ElementsAPI: React.FC = () => {
1616

1717
return (
1818
<Box flex={1} overflowY={layout !== 'stacked' ? 'hidden' : undefined}>
19-
<API apiDescriptionUrl={specUrlWithProxy} router="hash" layout={layout} />
19+
<API apiDescriptionUrl={specUrlWithProxy} router="history" layout={layout} />
2020
</Box>
2121
);
2222
};

demo/webpack/common.js

+2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ const HtmlWebpackPlugin = require('html-webpack-plugin');
55

66
const absoluteElementsPath = resolve(__dirname, '../../packages/elements/src');
77
const absoluteElementsCorePath = resolve(__dirname, '../../packages/elements-core/src');
8+
const absoluteElementsDevPortalPath = resolve(__dirname, '../../packages/elements-dev-portal/src');
89

910
console.log(absoluteElementsPath);
1011

@@ -15,6 +16,7 @@ module.exports = {
1516
alias: {
1617
'@stoplight/elements': absoluteElementsPath,
1718
'@stoplight/elements-core': absoluteElementsCorePath,
19+
'@stoplight/elements-dev-portal': absoluteElementsDevPortalPath,
1820
},
1921
fallback: {
2022
stream: false,

examples/angular/src/app/stoplight-project/stoplight-project.component.html

+1
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@
22
[platformUrl]="platformUrl"
33
[projectId]="projectId"
44
[basePath]="basePath"
5+
[router]="router"
56
></elements-stoplight-project>

examples/angular/src/app/stoplight-project/stoplight-project.component.ts

+1
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,5 @@ export class StoplightProjectComponent {
1010
projectId = 'cHJqOjYwNjYx';
1111
platformUrl = 'https://stoplight.io';
1212
basePath = environment.basePath ? `${environment.basePath}/stoplight-project` : 'stoplight-project';
13+
router = 'history';
1314
}

examples/react-cra/package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@
1212
"dependencies": {
1313
"@stoplight/elements": "^7.0.0",
1414
"@stoplight/elements-dev-portal": "^1.0.0",
15-
"react": "^17.0.2"
15+
"react": "^17.0.2",
16+
"react-router-dom": "^6.28.0"
1617
},
1718
"devDependencies": {
1819
"@types/node": "^17.0.11",

examples/react-cra/src/App.tsx

+7-9
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { DevPortalProvider } from '@stoplight/elements-dev-portal';
22
import React, { Component } from 'react';
3-
import { BrowserRouter, Redirect, Route, Switch } from 'react-router-dom';
3+
import { BrowserRouter, Navigate, Route, Routes } from 'react-router-dom';
44

55
import { StoplightAPI } from './components/API';
66
import { Navigation } from './components/Navigation';
@@ -17,14 +17,12 @@ class App extends Component {
1717
<Navigation />
1818
</header>
1919
<main className="main-content">
20-
<Switch>
21-
<Route exact path="/">
22-
<Redirect to="/stoplight-project" />
23-
</Route>
24-
<Route path="/zoom-api" component={StoplightAPI} />
25-
<Route path="/stoplight-project" component={StoplightProjectDocs} />
26-
<Route component={NotFound} />
27-
</Switch>
20+
<Routes>
21+
<Route path="/zoom-api/*" element={<StoplightAPI />} />
22+
<Route path="/stoplight-project/*" element={<StoplightProjectDocs />} />
23+
<Route path="/" element={<Navigate to="stoplight-project" replace />} />
24+
<Route element={<NotFound />} />
25+
</Routes>
2826
</main>
2927
</div>
3028
</BrowserRouter>

examples/react-cra/src/components/API.tsx

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import React from 'react';
66
export const StoplightAPI: React.FC = () => {
77
return (
88
<API
9+
router="history"
910
basePath="zoom-api"
1011
apiDescriptionUrl="https://raw.githubusercontent.com/stoplightio/Public-APIs/master/reference/zoom/openapi.yaml"
1112
/>

examples/react-cra/src/components/StoplightProject.tsx

+8-1
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,12 @@ import { StoplightProject } from '@stoplight/elements-dev-portal';
44
import React from 'react';
55

66
export const StoplightProjectDocs: React.FC = () => {
7-
return <StoplightProject basePath="stoplight-project" platformUrl="https://stoplight.io" projectId="cHJqOjYwNjYx" />;
7+
return (
8+
<StoplightProject
9+
router="history"
10+
basePath="stoplight-project"
11+
platformUrl="https://stoplight.io"
12+
projectId="cHJqOjYwNjYx"
13+
/>
14+
);
815
};

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
"@storybook/addon-styling-webpack": "0.0.5",
2525
"@storybook/addon-toolbars": "7.5.3",
2626
"@storybook/addon-viewport": "7.5.3",
27+
"@storybook/addons": "7.5.3",
2728
"@storybook/react": "7.5.3",
2829
"@storybook/react-webpack5": "7.5.3",
2930
"@storybook/theming": "7.5.3",

packages/elements-core/package.json

+2-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@stoplight/elements-core",
3-
"version": "8.5.2",
3+
"version": "9.0.0",
44
"sideEffects": [
55
"web-components.min.js",
66
"src/web-components/**",
@@ -79,8 +79,7 @@
7979
"nanoid": "^3.1.32",
8080
"prop-types": "^15.7.2",
8181
"react-query": "^3.34.19",
82-
"react-router-dom": "^5.2.0",
83-
"react-router-hash-link": "^2.1.0",
82+
"react-router-dom": "^6.28.0",
8483
"tslib": "^2.1.0",
8584
"urijs": "^1.19.11",
8685
"util": "^0.12.4",

packages/elements-core/src/components/Docs/Article/Article.spec.tsx

+1-2
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,9 @@ describe('Article', () => {
2929
});
3030

3131
it('given hash router, should have correct links', () => {
32-
location.hash = '#/test';
3332
const { unmount } = render(<ArticleWithRouter router="hash" data="[abc](#abc)" />);
3433

35-
expect(screen.getByText(/abc/i)).toHaveAttribute('href', '#/test#abc');
34+
expect(screen.getByText(/abc/i)).toHaveAttribute('href', '#abc');
3635

3736
unmount();
3837
});

packages/elements-core/src/components/Layout/ResponsiveSidebarLayout.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { useResponsiveLayout } from '../../hooks/useResponsiveLayout';
66
import { LogoProps } from '../../types';
77
import { Logo } from '../Logo';
88
import { PoweredByLink } from '../PoweredByLink';
9-
import type { TableOfContentsItem } from '../TableOfContents';
9+
import type { CustomLinkComponent, TableOfContentsItem } from '../TableOfContents';
1010
import { TableOfContents } from '../TableOfContents';
1111
import { MobileTopNav } from './MobileTopNav';
1212

@@ -129,7 +129,7 @@ export const Sidebar = ({
129129
<TableOfContents
130130
tree={tree}
131131
activeId={pathname}
132-
Link={Link}
132+
Link={Link as CustomLinkComponent}
133133
onLinkClick={onTocClick}
134134
isInResponsiveMode={isInResponsiveMode}
135135
/>

packages/elements-core/src/components/MarkdownViewer/CustomComponents/ReactRouterLink.tsx

+2-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import type { LinkProps } from '@stoplight/mosaic';
22
import React from 'react';
3-
import { HashLink } from 'react-router-hash-link';
43

54
const externalRegex = new RegExp('^(?:[a-z]+:)?//', 'i');
65

@@ -21,8 +20,8 @@ export const ReactRouterMarkdownLink = ({
2120
);
2221
}
2322
return (
24-
<HashLink to={href} title={title}>
23+
<a href={href as string} title={title}>
2524
{children}
26-
</HashLink>
25+
</a>
2726
);
2827
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
/*
2+
* This is copied from https://github.com/ncoughlin/scroll-to-hash-element/blob/main/src/index.tsx
3+
* Unfortunately this package does not contain typescript definitions, so we can not use it directly.
4+
*/
5+
import { useEffect, useLayoutEffect, useRef, useState } from 'react';
6+
7+
interface ScrollToHashElementProps {
8+
/** On first run do we jump or scroll? */
9+
initialBehavior?: ScrollBehavior;
10+
behavior?: ScrollBehavior;
11+
inline?: ScrollLogicalPosition;
12+
block?: ScrollLogicalPosition;
13+
}
14+
15+
export const ScrollToHashElement = ({
16+
behavior = 'auto',
17+
initialBehavior = 'auto',
18+
inline = 'nearest',
19+
block = 'start',
20+
}: ScrollToHashElementProps): null => {
21+
const [hash, setHash] = useState(window.location.hash);
22+
const [count, setCount] = useState(0);
23+
const originalListeners = useRef<{ [key: string]: Function }>({});
24+
25+
// We need to know if this is the first run. If it is, we can do an instant jump, no scrolling.
26+
const [firstRun, setFirstRun] = useState(true);
27+
useEffect(() => setFirstRun(false), []);
28+
29+
useEffect(() => {
30+
const handleLocationChange = () => {
31+
setHash(window.location.hash);
32+
33+
// We increment count just so the layout effect will run if the hash is the same.
34+
// Otherwise the user might click a hashlink a second time and it won't go anywhere.
35+
setCount((count: number) => count + 1);
36+
};
37+
38+
const onPopState = () => {
39+
window.dispatchEvent(new Event('locationchange'));
40+
};
41+
42+
const addWindowListeners = () => {
43+
originalListeners.current.pushState = window.history.pushState;
44+
originalListeners.current.replaceState = window.history.replaceState;
45+
46+
window.history.pushState = function (...args: any) {
47+
const result = originalListeners.current.pushState.apply(this, args);
48+
window.dispatchEvent(new Event('pushstate'));
49+
window.dispatchEvent(new Event('locationchange'));
50+
return result;
51+
};
52+
53+
window.history.replaceState = function (...args: any) {
54+
const result = originalListeners.current.replaceState.apply(this, args);
55+
window.dispatchEvent(new Event('replacestate'));
56+
window.dispatchEvent(new Event('locationchange'));
57+
return result;
58+
};
59+
60+
window.addEventListener('popstate', onPopState);
61+
window.addEventListener('locationchange', handleLocationChange);
62+
};
63+
64+
// Cleanup the event listeners on component unmount
65+
const removeWindowListeners = () => {
66+
window.history.pushState = originalListeners.current.pushState as typeof window.history.pushState;
67+
window.history.replaceState = originalListeners.current.replaceState as typeof window.history.replaceState;
68+
window.removeEventListener('popstate', onPopState);
69+
window.removeEventListener('locationchange', handleLocationChange);
70+
};
71+
72+
addWindowListeners();
73+
return removeWindowListeners;
74+
}, []);
75+
76+
useLayoutEffect(() => {
77+
const removeHashCharacter = (str: string) => {
78+
return str.slice(1);
79+
};
80+
81+
if (hash) {
82+
const element = document.getElementById(removeHashCharacter(hash));
83+
84+
if (element) {
85+
element.scrollIntoView({
86+
behavior: firstRun ? initialBehavior : behavior,
87+
inline: inline,
88+
block: block,
89+
});
90+
}
91+
}
92+
// eslint-disable-next-line react-hooks/exhaustive-deps
93+
}, [hash, count, firstRun]);
94+
95+
return null;
96+
};

0 commit comments

Comments
 (0)