diff --git a/src/App.test.tsx b/src/App.test.tsx
index 8adad4bd2..2801facca 100644
--- a/src/App.test.tsx
+++ b/src/App.test.tsx
@@ -1,9 +1,15 @@
import { render, screen } from '@testing-library/react'
import { App } from './App'
import { describe, test, vi } from 'vitest'
-import { BrowserRouter } from 'react-router-dom'
+import {
+ Route,
+ RouterProvider,
+ createMemoryRouter,
+ createRoutesFromElements,
+} from 'react-router-dom'
import { GlobalStateProvider } from './components/GlobalStateProvider'
import CommandBarProvider from 'components/CommandBar'
+import { BROWSER_FILE_NAME } from 'Router'
let listener: ((rect: any) => void) | undefined = undefined
;(global as any).ResizeObserver = class ResizeObserver {
@@ -24,7 +30,7 @@ describe('App tests', () => {
>
return {
...actual,
- useParams: () => ({ id: 'new' }),
+ useParams: () => ({ id: BROWSER_FILE_NAME }),
useLoaderData: () => ({ code: null }),
}
})
@@ -41,12 +47,24 @@ describe('App tests', () => {
})
function TestWrap({ children }: { children: React.ReactNode }) {
- // wrap in router and xState context
- return (
-
-
- {children}
-
-
+ // We have to use a memory router in the testing environment,
+ // and we have to use the createMemoryRouter function instead of as of react-router v6.4:
+ // https://reactrouter.com/en/6.16.0/routers/picking-a-router#using-v64-data-apis
+ const router = createMemoryRouter(
+ createRoutesFromElements(
+
+ {children}
+
+ }
+ />
+ ),
+ {
+ initialEntries: ['/file/new'],
+ initialIndex: 0,
+ }
)
+ return
}
diff --git a/src/Router.tsx b/src/Router.tsx
index 6b28c2630..05a1a747e 100644
--- a/src/Router.tsx
+++ b/src/Router.tsx
@@ -94,6 +94,8 @@ export const paths = {
) as typeof onboardingPaths,
}
+export const BROWSER_FILE_NAME = 'new'
+
export type IndexLoaderData = {
code: string | null
project?: ProjectWithEntryPointMetadata
@@ -129,7 +131,9 @@ const router = createBrowserRouter(
{
path: paths.INDEX,
loader: () =>
- isTauri() ? redirect(paths.HOME) : redirect(paths.FILE + '/new'),
+ isTauri()
+ ? redirect(paths.HOME)
+ : redirect(paths.FILE + '/' + BROWSER_FILE_NAME),
errorElement: ,
},
{
@@ -167,7 +171,7 @@ const router = createBrowserRouter(
)
}
- if (params.id && params.id !== 'new') {
+ if (params.id && params.id !== BROWSER_FILE_NAME) {
// Note that PROJECT_ENTRYPOINT is hardcoded until we support multiple files
const code = await readTextFile(params.id + '/' + PROJECT_ENTRYPOINT)
const entrypoint_metadata = await metadata(
@@ -212,7 +216,7 @@ const router = createBrowserRouter(
),
loader: async () => {
if (!isTauri()) {
- return redirect(paths.FILE + '/new')
+ return redirect(paths.FILE + '/' + BROWSER_FILE_NAME)
}
const fetchedStorage = localStorage?.getItem(SETTINGS_PERSIST_KEY)
const persistedSettings = JSON.parse(fetchedStorage || '{}') as Partial<
diff --git a/src/components/ErrorPage.tsx b/src/components/ErrorPage.tsx
index d6576db9d..bf8f536d9 100644
--- a/src/components/ErrorPage.tsx
+++ b/src/components/ErrorPage.tsx
@@ -47,11 +47,9 @@ export const ErrorPage = () => {
Clear storage
Report Bug
diff --git a/src/components/UserSidebarMenu.test.tsx b/src/components/UserSidebarMenu.test.tsx
index 6389c4499..0ce3e52db 100644
--- a/src/components/UserSidebarMenu.test.tsx
+++ b/src/components/UserSidebarMenu.test.tsx
@@ -1,6 +1,11 @@
import { fireEvent, render, screen } from '@testing-library/react'
import UserSidebarMenu from './UserSidebarMenu'
-import { BrowserRouter } from 'react-router-dom'
+import {
+ Route,
+ RouterProvider,
+ createMemoryRouter,
+ createRoutesFromElements,
+} from 'react-router-dom'
import { Models } from '@kittycad/lib'
import { GlobalStateProvider } from './GlobalStateProvider'
import CommandBarProvider from './CommandBar'
@@ -93,11 +98,24 @@ describe('UserSidebarMenu tests', () => {
function TestWrap({ children }: { children: React.ReactNode }) {
// wrap in router and xState context
- return (
-
-
- {children}
-
-
+ // We have to use a memory router in the testing environment,
+ // and we have to use the createMemoryRouter function instead of as of react-router v6.4:
+ // https://reactrouter.com/en/6.16.0/routers/picking-a-router#using-v64-data-apis
+ const router = createMemoryRouter(
+ createRoutesFromElements(
+
+ {children}
+
+ }
+ />
+ ),
+ {
+ initialEntries: ['/file/new'],
+ initialIndex: 0,
+ }
)
+ return
}
diff --git a/src/components/UserSidebarMenu.tsx b/src/components/UserSidebarMenu.tsx
index 179a98d4a..6f26a3e1d 100644
--- a/src/components/UserSidebarMenu.tsx
+++ b/src/components/UserSidebarMenu.tsx
@@ -7,17 +7,17 @@ import {
faSignOutAlt,
} from '@fortawesome/free-solid-svg-icons'
import { faGithub } from '@fortawesome/free-brands-svg-icons'
-import { useLocation, useNavigate } from 'react-router-dom'
+import { useNavigate } from 'react-router-dom'
import { Fragment, useState } from 'react'
import { paths } from '../Router'
-import makeUrlPathRelative from '../lib/makeUrlPathRelative'
import { Models } from '@kittycad/lib'
import { useGlobalStateContext } from 'hooks/useGlobalStateContext'
+import { useAbsoluteFilePath } from 'hooks/useAbsoluteFilePath'
type User = Models['User_type']
const UserSidebarMenu = ({ user }: { user?: User }) => {
- const location = useLocation()
+ const filePath = useAbsoluteFilePath()
const displayedName = getDisplayName(user)
const [imageLoadFailed, setImageLoadFailed] = useState(false)
const navigate = useNavigate()
@@ -132,11 +132,7 @@ const UserSidebarMenu = ({ user }: { user?: User }) => {
// since /settings is a nested route the sidebar doesn't close
// automatically when navigating to it
close()
- navigate(
- (location.pathname.endsWith('/')
- ? location.pathname.slice(0, -1)
- : location.pathname) + paths.SETTINGS
- )
+ navigate(filePath + paths.SETTINGS)
}}
>
Settings
diff --git a/src/hooks/useAbsoluteFilePath.ts b/src/hooks/useAbsoluteFilePath.ts
new file mode 100644
index 000000000..6de2c2082
--- /dev/null
+++ b/src/hooks/useAbsoluteFilePath.ts
@@ -0,0 +1,12 @@
+import { BROWSER_FILE_NAME, IndexLoaderData, paths } from 'Router'
+import { useRouteLoaderData } from 'react-router-dom'
+
+export function useAbsoluteFilePath() {
+ const routeData = useRouteLoaderData(paths.FILE) as IndexLoaderData
+
+ return (
+ paths.FILE +
+ '/' +
+ encodeURIComponent(routeData?.project?.path || BROWSER_FILE_NAME)
+ )
+}
diff --git a/src/routes/Onboarding/index.tsx b/src/routes/Onboarding/index.tsx
index dc8c460e2..d82b5a3c3 100644
--- a/src/routes/Onboarding/index.tsx
+++ b/src/routes/Onboarding/index.tsx
@@ -1,5 +1,5 @@
import { useHotkeys } from 'react-hotkeys-hook'
-import { Outlet, useRouteLoaderData, useNavigate } from 'react-router-dom'
+import { Outlet, useNavigate } from 'react-router-dom'
import Introduction from './Introduction'
import Camera from './Camera'
import Sketching from './Sketching'
@@ -15,7 +15,8 @@ import UserMenu from './UserMenu'
import ProjectMenu from './ProjectMenu'
import Export from './Export'
import FutureWork from './FutureWork'
-import { IndexLoaderData, paths } from 'Router'
+import { paths } from 'Router'
+import { useAbsoluteFilePath } from 'hooks/useAbsoluteFilePath'
export const onboardingPaths = {
INDEX: '/',
@@ -86,29 +87,23 @@ export const onboardingRoutes = [
]
export function useNextClick(newStatus: string) {
+ const filePath = useAbsoluteFilePath()
const {
settings: { send },
} = useGlobalStateContext()
const navigate = useNavigate()
- const { project } = useRouteLoaderData(paths.FILE) as IndexLoaderData
return useCallback(() => {
send({
type: 'Set Onboarding Status',
data: { onboardingStatus: newStatus },
})
- navigate(
- paths.FILE +
- '/' +
- encodeURIComponent(project?.path || 'new') +
- paths.ONBOARDING.INDEX.slice(0, -1) +
- newStatus
- )
- }, [project, newStatus, send, navigate])
+ navigate(filePath + paths.ONBOARDING.INDEX.slice(0, -1) + newStatus)
+ }, [filePath, newStatus, send, navigate])
}
export function useDismiss() {
- const routeData = useRouteLoaderData(paths.FILE) as IndexLoaderData
+ const filePath = useAbsoluteFilePath()
const {
settings: { send },
} = useGlobalStateContext()
@@ -119,10 +114,8 @@ export function useDismiss() {
type: 'Set Onboarding Status',
data: { onboardingStatus: 'dismissed' },
})
- navigate(
- paths.FILE + '/' + encodeURIComponent(routeData?.project?.path || 'new')
- )
- }, [send, navigate, routeData])
+ navigate(filePath)
+ }, [send, navigate, filePath])
}
const Onboarding = () => {