diff --git a/examples/official-storybook/package.json b/examples/official-storybook/package.json index d6fda70c4ff4..ca460e05209a 100644 --- a/examples/official-storybook/package.json +++ b/examples/official-storybook/package.json @@ -35,6 +35,7 @@ "@storybook/jest": "0.0.0-alpha.5", "@storybook/node-logger": "6.4.0-beta.19", "@storybook/react": "6.4.0-beta.19", + "@storybook/router": "6.4.0-beta.19", "@storybook/source-loader": "6.4.0-beta.19", "@storybook/testing-library": "0.0.0-alpha.3", "@storybook/theming": "6.4.0-beta.19", diff --git a/lib/api/package.json b/lib/api/package.json index 7d071631af16..f5488c8e4290 100644 --- a/lib/api/package.json +++ b/lib/api/package.json @@ -38,7 +38,6 @@ "prepare": "node ../../scripts/prepare.js" }, "dependencies": { - "@reach/router": "^1.3.4", "@storybook/channels": "6.4.0-beta.19", "@storybook/client-logger": "6.4.0-beta.19", "@storybook/core-events": "6.4.0-beta.19", @@ -46,13 +45,11 @@ "@storybook/router": "6.4.0-beta.19", "@storybook/semver": "^7.3.2", "@storybook/theming": "6.4.0-beta.19", - "@types/reach__router": "^1.3.7", "core-js": "^3.8.2", "fast-deep-equal": "^3.1.3", "global": "^4.4.0", "lodash": "^4.17.20", "memoizerific": "^1.11.3", - "qs": "^6.10.0", "regenerator-runtime": "^0.13.7", "store2": "^2.12.0", "telejson": "^5.3.2", diff --git a/lib/api/src/index.tsx b/lib/api/src/index.tsx index 5cc04a324247..8ad74a916a32 100644 --- a/lib/api/src/index.tsx +++ b/lib/api/src/index.tsx @@ -18,7 +18,7 @@ import { SHARED_STATE_SET, SET_STORIES, } from '@storybook/core-events'; -import { RenderData as RouterData } from '@storybook/router'; +import { RouterData } from '@storybook/router'; import { Listener } from '@storybook/channels'; import { createContext } from './context'; diff --git a/lib/api/src/modules/addons.ts b/lib/api/src/modules/addons.ts index a74a007da24f..7dbab9bd19c7 100644 --- a/lib/api/src/modules/addons.ts +++ b/lib/api/src/modules/addons.ts @@ -1,5 +1,5 @@ import { ReactElement } from 'react'; -import { WindowLocation } from '@reach/router'; +import type { RenderData } from '@storybook/router'; import deprecate from 'util-deprecate'; import dedent from 'ts-dedent'; @@ -35,13 +35,13 @@ export interface RenderOptions { export interface RouteOptions { storyId: string; viewMode: ViewMode; - location: WindowLocation; + location: RenderData['location']; path: string; } export interface MatchOptions { storyId: string; viewMode: ViewMode; - location: WindowLocation; + location: RenderData['location']; path: string; } diff --git a/lib/api/src/modules/url.ts b/lib/api/src/modules/url.ts index 59d057cf5a63..7062acd9883b 100644 --- a/lib/api/src/modules/url.ts +++ b/lib/api/src/modules/url.ts @@ -1,4 +1,3 @@ -import { navigate as navigateRouter, NavigateOptions } from '@reach/router'; import { once } from '@storybook/client-logger'; import { NAVIGATE_URL, @@ -6,7 +5,7 @@ import { SET_CURRENT_STORY, GLOBALS_UPDATED, } from '@storybook/core-events'; -import { queryFromLocation, navigate as queryNavigate, buildArgsParam } from '@storybook/router'; +import { queryFromLocation, buildArgsParam, NavigateOptions } from '@storybook/router'; import { toId, sanitize } from '@storybook/csf'; import deepEqual from 'fast-deep-equal'; import global from 'global'; @@ -28,15 +27,6 @@ const parseBoolean = (value: string) => { return undefined; }; -const navigateTo = (path: string, queryParams: Record = {}, options = {}) => { - const params = Object.entries(queryParams) - .filter(([, v]) => v) - .sort(([a], [b]) => (a < b ? -1 : 1)) - .map(([k, v]) => `${k}=${v}`); - const to = [path, ...params].join('&'); - return queryNavigate(to, options); -}; - // Initialize the state based on the URL. // NOTE: // Although we don't change the URL when you change the state, we do support setting initial state @@ -130,7 +120,7 @@ export interface QueryParams { } export interface SubAPI { - navigateUrl: (url: string, options: NavigateOptions<{}>) => void; + navigateUrl: (url: string, options: NavigateOptions) => void; getQueryParam: (key: string) => string | undefined; getUrlState: () => { queryParams: QueryParams; @@ -143,6 +133,15 @@ export interface SubAPI { } export const init: ModuleFn = ({ store, navigate, state, provider, fullAPI, ...rest }) => { + const navigateTo = (path: string, queryParams: Record = {}, options = {}) => { + const params = Object.entries(queryParams) + .filter(([, v]) => v) + .sort(([a], [b]) => (a < b ? -1 : 1)) + .map(([k, v]) => `${k}=${v}`); + const to = [path, ...params].join('&'); + return navigate(to, options); + }; + const api: SubAPI = { getQueryParam(key) { const { customQueryParams } = store.getState(); @@ -167,8 +166,8 @@ export const init: ModuleFn = ({ store, navigate, state, provider, fullAPI, ...r const equal = deepEqual(customQueryParams, update); if (!equal) store.setState({ customQueryParams: update }); }, - navigateUrl(url: string, options: NavigateOptions<{}>) { - navigateRouter(url, options); + navigateUrl(url, options) { + navigate(url, { ...options, plain: true }); }, }; diff --git a/lib/api/src/tests/url.test.js b/lib/api/src/tests/url.test.js index 7ea83c84bd7e..e3b863eef7df 100644 --- a/lib/api/src/tests/url.test.js +++ b/lib/api/src/tests/url.test.js @@ -1,12 +1,10 @@ import qs from 'qs'; import { SET_CURRENT_STORY, GLOBALS_UPDATED } from '@storybook/core-events'; -import { navigate as reachNavigate } from '@reach/router'; import { init as initURL } from '../modules/url'; jest.mock('@storybook/client-logger'); -jest.mock('@reach/router'); jest.useFakeTimers(); describe('initial state', () => { @@ -193,7 +191,9 @@ describe('initModule', () => { it('updates args param on SET_CURRENT_STORY', async () => { store.setState(storyState('test--story')); - const { api, init } = initURL({ store, state: { location: {} }, fullAPI }); + const navigate = jest.fn(); + + const { api, init } = initURL({ store, state: { location: {} }, navigate, fullAPI }); Object.assign(fullAPI, api, { getCurrentStoryData: () => ({ args: { a: 1, b: 2 }, @@ -204,8 +204,8 @@ describe('initModule', () => { init(); fullAPI.emit(SET_CURRENT_STORY); - expect(reachNavigate).toHaveBeenCalledWith( - '/?path=/story/test--story&args=b:2', + expect(navigate).toHaveBeenCalledWith( + '/story/test--story&args=b:2', expect.objectContaining({ replace: true }) ); expect(store.getState().customQueryParams).toEqual({ args: 'b:2' }); @@ -214,13 +214,15 @@ describe('initModule', () => { it('updates globals param on GLOBALS_UPDATED', async () => { store.setState(storyState('test--story')); - const { api, init } = initURL({ store, state: { location: {} }, fullAPI }); + const navigate = jest.fn(); + + const { api, init } = initURL({ store, state: { location: {} }, navigate, fullAPI }); Object.assign(fullAPI, api); init(); fullAPI.emit(GLOBALS_UPDATED, { globals: { a: 2 }, initialGlobals: { a: 1, b: 1 } }); - expect(reachNavigate).toHaveBeenCalledWith( - '/?path=/story/test--story&globals=a:2;b:!undefined', + expect(navigate).toHaveBeenCalledWith( + '/story/test--story&globals=a:2;b:!undefined', expect.objectContaining({ replace: true }) ); expect(store.getState().customQueryParams).toEqual({ globals: 'a:2;b:!undefined' }); @@ -228,22 +230,23 @@ describe('initModule', () => { it('adds url params alphabetically', async () => { store.setState({ ...storyState('test--story'), customQueryParams: { full: 1 } }); + const navigate = jest.fn(); - const { api, init } = initURL({ store, state: { location: {} }, fullAPI }); + const { api, init } = initURL({ store, state: { location: {} }, navigate, fullAPI }); Object.assign(fullAPI, api, { getCurrentStoryData: () => ({ args: { a: 1 }, isLeaf: true }), }); init(); fullAPI.emit(GLOBALS_UPDATED, { globals: { g: 2 } }); - expect(reachNavigate).toHaveBeenCalledWith( - '/?path=/story/test--story&full=1&globals=g:2', + expect(navigate).toHaveBeenCalledWith( + '/story/test--story&full=1&globals=g:2', expect.objectContaining({ replace: true }) ); fullAPI.emit(SET_CURRENT_STORY); - expect(reachNavigate).toHaveBeenCalledWith( - '/?path=/story/test--story&args=a:1&full=1&globals=g:2', + expect(navigate).toHaveBeenCalledWith( + '/story/test--story&args=a:1&full=1&globals=g:2', expect.objectContaining({ replace: true }) ); }); diff --git a/lib/core-common/src/types.ts b/lib/core-common/src/types.ts index 79e600d5c641..4aeed0b0f064 100644 --- a/lib/core-common/src/types.ts +++ b/lib/core-common/src/types.ts @@ -261,10 +261,13 @@ export type Preset = */ export type Entry = string; -type StorybookRefs = Record; +type StorybookRefs = Record< + string, + { + title: string; + url: string; + } +>; /** * The interface for Storybook configuration in `main.ts` files. @@ -337,7 +340,7 @@ export interface StorybookConfig { /** * References external Storybooks */ - refs?: StorybookRefs | ((config: Configuration, options: Options) => StorybookRefs) + refs?: StorybookRefs | ((config: Configuration, options: Options) => StorybookRefs); /** * Modify or return a custom Webpack config. diff --git a/lib/router/README.md b/lib/router/README.md index e356bd469fe7..d820bd39f10c 100644 --- a/lib/router/README.md +++ b/lib/router/README.md @@ -1,5 +1,5 @@ # Storybook Router -Storybook Router is a wrapper library for reach/router. +Storybook Router is a wrapper library for react-router. It ensures a single version of the router is used everywhere. It also includes some ready to use utils to read the path, query, viewMode and storyId from location. diff --git a/lib/router/package.json b/lib/router/package.json index 7f4724cf8ee2..2e16a75b0c09 100644 --- a/lib/router/package.json +++ b/lib/router/package.json @@ -40,15 +40,16 @@ "prepare": "node ../../scripts/prepare.js" }, "dependencies": { - "@reach/router": "^1.3.4", "@storybook/client-logger": "6.4.0-beta.19", - "@types/reach__router": "^1.3.7", "core-js": "^3.8.2", "fast-deep-equal": "^3.1.3", "global": "^4.4.0", + "history": "^5.0.1", "lodash": "^4.17.20", "memoizerific": "^1.11.3", "qs": "^6.10.0", + "react-router": "^6.0.0-beta.7", + "react-router-dom": "^6.0.0-beta.7", "ts-dedent": "^2.0.0" }, "peerDependencies": { diff --git a/lib/router/src/router.tsx b/lib/router/src/router.tsx index 8ee8e1b85bc9..d6cf9bcc8aa1 100644 --- a/lib/router/src/router.tsx +++ b/lib/router/src/router.tsx @@ -1,17 +1,14 @@ import global from 'global'; -import React, { ReactNode } from 'react'; +import React, { ReactNode, useCallback } from 'react'; import { Link, - Location, - navigate, - LocationProvider, - RouteComponentProps, - LocationContext, - NavigateFn, + BrowserRouter, + useNavigate, + useLocation, NavigateOptions, - History, -} from '@reach/router'; + Router, +} from 'react-router-dom'; import { ToggleVisibility } from './visibility'; import { queryFromString, parsePath, getMatch, StoryData } from './utils'; @@ -22,9 +19,12 @@ interface Other extends StoryData { singleStory?: boolean; } -export type RenderData = Pick & - Partial> & - Other; +export type RouterData = { + location: Partial; + navigate: ReturnType; +} & Other; + +export type RenderData = Pick & Other; interface MatchingData { match: null | { path: string }; @@ -52,8 +52,26 @@ export interface QueryLinkProps { const getBase = () => `${document.location.pathname}?`; -const queryNavigate: NavigateFn = (to: string | number, options?: NavigateOptions<{}>) => - typeof to === 'number' ? navigate(to) : navigate(`${getBase()}path=${to}`, options); +type ExpandedNavigateOptions = NavigateOptions & { plain?: boolean }; + +// const queryNavigate: NavigateFn = (to: string | number, options?: NavigateOptions<{}>) => +// typeof to === 'number' ? navigate(to) : navigate(`${getBase()}path=${to}`, options); + +const useQueryNavigate = () => { + const navigate = useNavigate(); + + return useCallback((to: string | number, options?: ExpandedNavigateOptions) => { + if (typeof to === 'string') { + const target = options?.plain ? to : `?path=${to}`; + return navigate(target, options); + } + if (typeof to === 'number') { + return navigate(to); + } + + return undefined; + }, []); +}; // A component that will navigate to a new location/path when clicked const QueryLink = ({ to, children, ...rest }: QueryLinkProps) => ( @@ -65,24 +83,24 @@ QueryLink.displayName = 'QueryLink'; // A render-prop component where children is called with a location // and will be called whenever it changes when it changes -const QueryLocation = ({ children }: QueryLocationProps) => ( - - {({ location }: RouteComponentProps): ReactNode => { - const { path, singleStory } = queryFromString(location.search); - const { viewMode, storyId, refId } = parsePath(path); +const QueryLocation = ({ children }: QueryLocationProps) => { + const location = useLocation(); + const { path, singleStory } = queryFromString(location.search); + const { viewMode, storyId, refId } = parsePath(path); - return children({ + return ( + <> + {children({ path, location, - navigate: queryNavigate, viewMode, storyId, refId, singleStory: singleStory === 'true', - }); - }} - -); + })} + + ); +}; QueryLocation.displayName = 'QueryLocation'; // A render-prop component for rendering when a certain path is hit. @@ -117,6 +135,10 @@ export { QueryLink as Link }; export { QueryMatch as Match }; export { QueryLocation as Location }; export { Route }; -export { queryNavigate as navigate }; -export { LocationProvider }; -export type { History }; +export { useQueryNavigate as useNavigate }; +export { BrowserRouter as LocationProvider }; +export { Router as BaseLocationProvider }; +export { useNavigate as usePlainNavigate }; + +// eslint-disable-next-line no-undef +export type { ExpandedNavigateOptions as NavigateOptions }; diff --git a/lib/router/src/utils.ts b/lib/router/src/utils.ts index 0831485f8e04..47d1e81f0f4f 100644 --- a/lib/router/src/utils.ts +++ b/lib/router/src/utils.ts @@ -139,7 +139,7 @@ interface Query { export const queryFromString = memoize(1000)( (s: string): Query => qs.parse(s, { ignoreQueryPrefix: true }) ); -export const queryFromLocation = (location: { search: string }) => queryFromString(location.search); +export const queryFromLocation = (location: Partial) => queryFromString(location.search); export const stringifyQuery = (query: Query) => qs.stringify(query, { addQueryPrefix: true, encode: false }); diff --git a/lib/ui/src/app.stories.tsx b/lib/ui/src/app.stories.tsx index dcede85e012b..29cce7d842b4 100644 --- a/lib/ui/src/app.stories.tsx +++ b/lib/ui/src/app.stories.tsx @@ -1,9 +1,10 @@ import React from 'react'; -import { createMemorySource, createHistory } from '@reach/router'; -import { Root as App } from './index'; +import { Provider as ManagerProvider } from '@storybook/api'; +import { LocationProvider } from '@storybook/router'; +import { HelmetProvider } from 'react-helmet-async'; +import App from './app'; import { PrettyFakeProvider, FakeProvider } from './FakeProvider'; -import Provider from './provider'; export default { title: 'UI/App', @@ -11,14 +12,67 @@ export default { parameters: { layout: 'fullscreen', }, + decorators: [ + (StoryFn) => ( + + + + + + ), + ], }; -const history = createHistory(createMemorySource('/?path=/story/story--id')); - export const Default = () => ( - + {}} + docsMode={false} + > + + ); export const LoadingState = () => ( - + {}} + docsMode={false} + > + + ); diff --git a/lib/ui/src/components/notifications/NotificationItem.stories.js b/lib/ui/src/components/notifications/NotificationItem.stories.js index 2fc9ac07bcb0..6c7ca43bb251 100644 --- a/lib/ui/src/components/notifications/NotificationItem.stories.js +++ b/lib/ui/src/components/notifications/NotificationItem.stories.js @@ -1,10 +1,18 @@ import React from 'react'; +import { LocationProvider } from '@storybook/router'; import NotificationItem from './NotificationItem'; export default { component: NotificationItem, title: 'UI/Notifications/NotificationItem', - decorators: [(storyFn) =>
{storyFn()}
], + decorators: [ + (StoryFn) => ( + + + + ), + (storyFn) =>
{storyFn()}
, + ], excludeStories: /.*Data$/, }; diff --git a/lib/ui/src/components/notifications/NotificationList.stories.js b/lib/ui/src/components/notifications/NotificationList.stories.js index 943ab0f985d1..87f2b8bf2769 100644 --- a/lib/ui/src/components/notifications/NotificationList.stories.js +++ b/lib/ui/src/components/notifications/NotificationList.stories.js @@ -1,4 +1,5 @@ import React from 'react'; +import { LocationProvider } from '@storybook/router'; import NotificationList from './NotificationList'; import itemMeta, * as itemStories from './NotificationItem.stories'; @@ -7,6 +8,12 @@ export default { component: NotificationList, title: 'UI/Notifications/NotificationList', decorators: [ + (StoryFn) => ( + + + + ), + (storyFn) => (
{storyFn()} diff --git a/lib/ui/src/components/preview/preview.stories.tsx b/lib/ui/src/components/preview/preview.stories.tsx index 5a856c027211..5c41c8142b8c 100644 --- a/lib/ui/src/components/preview/preview.stories.tsx +++ b/lib/ui/src/components/preview/preview.stories.tsx @@ -1,8 +1,9 @@ import React from 'react'; +import { parsePath, createPath } from 'history'; import { Provider as ManagerProvider, Combo, Consumer } from '@storybook/api'; -import { createMemorySource, createHistory } from '@reach/router'; -import { Location, LocationProvider } from '@storybook/router'; +import { Location, BaseLocationProvider } from '@storybook/router'; + import { ThemeProvider, ensure as ensureTheme, themes } from '@storybook/theming'; import { DecoratorFn } from '@storybook/react'; @@ -12,27 +13,65 @@ import { PrettyFakeProvider } from '../../FakeProvider'; import { previewProps } from './preview.mockdata'; const provider = new PrettyFakeProvider(); +const staticNavigator = { + createHref(to) { + return typeof to === 'string' ? to : createPath(to); + }, + + push() {}, + + replace() {}, + + go() {}, + + back() {}, + + forward() {}, +}; export default { title: 'UI/Preview', component: Preview, decorators: [ - ((StoryFn, c) => ( - - - {(locationData) => ( - - - - - - )} - - - )) as DecoratorFn, + ((StoryFn, c) => { + const locationProp = parsePath('/?path=/story/story--id'); + + const location = { + pathname: locationProp.pathname || '/', + search: locationProp.search || '', + hash: locationProp.hash || '', + state: null, + key: 'default', + }; + + return ( + + + {(locationData) => ( + {}} + > + + + + + )} + + + ); + }) as DecoratorFn, ], }; diff --git a/lib/ui/src/index.tsx b/lib/ui/src/index.tsx index 1ea275af269c..3ce49b2c55ed 100644 --- a/lib/ui/src/index.tsx +++ b/lib/ui/src/index.tsx @@ -1,8 +1,8 @@ import global from 'global'; -import React, { FunctionComponent } from 'react'; +import React, { FC, FunctionComponent } from 'react'; import ReactDOM from 'react-dom'; -import { Location, LocationProvider, History } from '@storybook/router'; +import { Location, LocationProvider, useNavigate } from '@storybook/router'; import { Provider as ManagerProvider, Combo } from '@storybook/api'; import { ThemeProvider, ensure as ensureTheme } from '@storybook/theming'; import { HelmetProvider } from 'react-helmet-async'; @@ -33,45 +33,53 @@ export interface RootProps { history?: History; } -export const Root: FunctionComponent = ({ provider, history }) => ( +export const Root: FunctionComponent = ({ provider }) => ( - - - {(locationData) => ( - - {({ state, api }: Combo) => { - const panelCount = Object.keys(api.getPanels()).length; - const story = api.getData(state.storyId, state.refId); - const isLoading = story - ? !!state.refs[state.refId] && !state.refs[state.refId].ready - : !state.storiesFailed && !state.storiesConfigured; - - return ( - - - - ); - }} - - )} - + +
); +const Main: FC<{ provider: Provider }> = ({ provider }) => { + const navigate = useNavigate(); + return ( + + {(locationData) => ( + + {({ state, api }: Combo) => { + const panelCount = Object.keys(api.getPanels()).length; + const story = api.getData(state.storyId, state.refId); + const isLoading = story + ? !!state.refs[state.refId] && !state.refs[state.refId].ready + : !state.storiesFailed && !state.storiesConfigured; + + return ( + + + + ); + }} + + )} + + ); +}; + function renderStorybookUI(domNode: HTMLElement, provider: Provider) { if (!(provider instanceof Provider)) { throw new Error('provider is not extended from the base Provider'); diff --git a/yarn.lock b/yarn.lock index f4a2b6393456..e1d6593ccfc8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7586,7 +7586,6 @@ __metadata: version: 0.0.0-use.local resolution: "@storybook/api@workspace:lib/api" dependencies: - "@reach/router": ^1.3.4 "@storybook/channels": 6.4.0-beta.19 "@storybook/client-logger": 6.4.0-beta.19 "@storybook/core-events": 6.4.0-beta.19 @@ -7595,7 +7594,6 @@ __metadata: "@storybook/semver": ^7.3.2 "@storybook/theming": 6.4.0-beta.19 "@types/lodash": ^4.14.167 - "@types/reach__router": ^1.3.7 "@types/semver": ^7.3.4 core-js: ^3.8.2 fast-deep-equal: ^3.1.3 @@ -7604,7 +7602,6 @@ __metadata: lodash: ^4.17.20 memoizerific: ^1.11.3 preval.macro: ^5.0.0 - qs: ^6.10.0 regenerator-runtime: ^0.13.7 store2: ^2.12.0 telejson: ^5.3.2 @@ -9024,15 +9021,16 @@ __metadata: version: 0.0.0-use.local resolution: "@storybook/router@workspace:lib/router" dependencies: - "@reach/router": ^1.3.4 "@storybook/client-logger": 6.4.0-beta.19 - "@types/reach__router": ^1.3.7 core-js: ^3.8.2 fast-deep-equal: ^3.1.3 global: ^4.4.0 + history: ^5.0.1 lodash: ^4.17.20 memoizerific: ^1.11.3 qs: ^6.10.0 + react-router: ^6.0.0-beta.7 + react-router-dom: ^6.0.0-beta.7 ts-dedent: ^2.0.0 peerDependencies: react: ^16.8.0 || ^17.0.0 @@ -25349,6 +25347,15 @@ fsevents@^1.2.7: languageName: node linkType: hard +"history@npm:^5.0.1": + version: 5.0.1 + resolution: "history@npm:5.0.1" + dependencies: + "@babel/runtime": ^7.7.6 + checksum: a36e20f3513acf5c3bd2c0edd5790cbf8ef6befb754595a7c99ee70b07afd6eef07396ec2f90f50e0602b580e4fbe6fe5950e812e4bb08fa541e296f9434a566 + languageName: node + linkType: hard + "hmac-drbg@npm:^1.0.1": version: 1.0.1 resolution: "hmac-drbg@npm:1.0.1" @@ -33480,6 +33487,7 @@ fsevents@^1.2.7: "@storybook/jest": 0.0.0-alpha.5 "@storybook/node-logger": 6.4.0-beta.19 "@storybook/react": 6.4.0-beta.19 + "@storybook/router": 6.4.0-beta.19 "@storybook/source-loader": 6.4.0-beta.19 "@storybook/testing-library": 0.0.0-alpha.3 "@storybook/theming": 6.4.0-beta.19 @@ -37956,6 +37964,29 @@ fsevents@^1.2.7: languageName: node linkType: hard +"react-router-dom@npm:^6.0.0-beta.7": + version: 6.0.0-beta.7 + resolution: "react-router-dom@npm:6.0.0-beta.7" + dependencies: + react-router: 6.0.0-beta.7 + peerDependencies: + history: ">=5" + react: ">=16.8" + react-dom: ">=16.8" + checksum: baf821316e5b1bb6f82f986e2a603123e5300264a036ea9ddeef4687c77daa4dd8a1fade4f890b6b1e5241745bc98791107cf8193596473e42be873ae6674dcc + languageName: node + linkType: hard + +"react-router@npm:6.0.0-beta.7, react-router@npm:^6.0.0-beta.7": + version: 6.0.0-beta.7 + resolution: "react-router@npm:6.0.0-beta.7" + peerDependencies: + history: ">=5" + react: ">=16.8" + checksum: f28071978b9329d3c3c998dd86a2e2ef59bfded6fa62976705959ffbfe6e0cb2b739f8dc364cd0deac69968444a3218c3cd94f48a4ed7753db91967321eeb9f1 + languageName: node + linkType: hard + "react-scripts@npm:3.4.4": version: 3.4.4 resolution: "react-scripts@npm:3.4.4"