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

Ensure updating the remix router also updates the navigation data #4273

Merged
merged 3 commits into from
Oct 2, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ import {
RemixNavigationBarHomeLabel,
RemixNavigationBarPathTestId,
} from '../../editor/remix-navigation-bar'
import { updateFromCodeEditor } from '../../editor/actions/actions-from-vscode'

const DefaultRouteTextContent = 'Hello Remix!'
const RootTextContent = 'This is root!'
Expand Down Expand Up @@ -350,6 +351,83 @@ describe('Remix content', () => {
).toHaveLength(1) // location not matching warning is rendered
})

it("changing a route whilst 404 is shown doesn't break the navigation", async () => {
const project = createModifiedProject({
[StoryboardFilePath]: `import * as React from 'react'
import { RemixScene, Storyboard } from 'utopia-api'

export var storyboard = (
<Storyboard data-uid='sb'>
<RemixScene
style={{
width: 700,
height: 759,
position: 'absolute',
left: 212,
top: 128,
}}
data-label='Playground'
data-uid='remix-scene'
/>
</Storyboard>
)
`,
['/src/root.js']: `import React from 'react'
import { Outlet } from '@remix-run/react'

export default function Root() {
return (
<div data-uid='rootdiv'>
${RootTextContent}
<Outlet data-uid='outlet'/>
</div>
)
}
`,
['/src/routes/_index.js']: `import React from 'react'
import { Link } from '@remix-run/react'

export default function Index() {
return <Link to='/about' data-testid='remix-link' data-uid='remix-link'>${DefaultRouteTextContent}</Link>
}
`,
})

const renderResult = await renderRemixProject(project)

await switchToLiveMode(renderResult)

await clickRemixLink(renderResult)

expect(renderResult.renderedDOM.queryByText('404 Not Found')).not.toBeNull() // default 404 page is rendered

// Make a change to a route, which will in turn update the router
const newRouteTextContent = 'I have changed!'
await renderResult.dispatch(
[
updateFromCodeEditor(
'/src/routes/_index.js',
`import React from 'react'
import { Link } from '@remix-run/react'

export default function Index() {
return <Link to='/about' data-testid='remix-link' data-uid='remix-link'>${newRouteTextContent}</Link>
}
`,
null,
),
],
true,
)
await renderResult.getDispatchFollowUpActionsFinished()

// Ensure we can still hit the back button and navigate back to the previous route, with the new content
const pathToRemixScene = EP.fromString('sb/remix-scene')
await navigateWithRemixSceneLabelButton(renderResult, pathToRemixScene, 'back')
expect(renderResult.renderedDOM.queryByText(newRouteTextContent)).not.toBeNull()
expect(getPathInRemixSceneLabel(renderResult, pathToRemixScene)).toEqual('(home)')
})

it('Two remix scenes, both have metadata', async () => {
const project = createModifiedProject({
[StoryboardFilePath]: `import * as React from 'react'
Expand Down
42 changes: 32 additions & 10 deletions editor/src/components/canvas/remix/utopia-remix-root-component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -215,16 +215,23 @@ export const UtopiaRemixRootComponent = React.memo((props: UtopiaRemixRootCompon
return createMemoryRouter(routes, { initialEntries: initialEntries })
}, [routes])

const updateNavigationData = React.useCallback(
(innerRouter: RouterType, location: Location) => {
const setNavigationDataForRouter = React.useCallback(
(
innerRouter: RouterType,
location: Location,
updateEntries: 'append-to-entries' | 'replace-entries',
) => {
setNavigationData((current) => {
const key = EP.toString(basePath)
const existingEntries = current[key]?.entries ?? []

const updatedEntries =
existingEntries.at(-1)?.pathname === location.pathname
? existingEntries
: existingEntries.concat(location)
const shouldUpdateEntries =
updateEntries === 'append-to-entries' &&
existingEntries.at(-1)?.pathname !== location.pathname

const updatedEntries = shouldUpdateEntries
? existingEntries.concat(location)
: existingEntries

return {
...current,
Expand All @@ -241,17 +248,32 @@ export const UtopiaRemixRootComponent = React.memo((props: UtopiaRemixRootCompon
[basePath, setNavigationData],
)

const updateNavigationData = React.useCallback(
(innerRouter: RouterType, location: Location) => {
setNavigationDataForRouter(innerRouter, location, 'append-to-entries')
},
[setNavigationDataForRouter],
)

const initialiseNavigationData = React.useCallback(
(innerRouter: RouterType) => {
setNavigationDataForRouter(innerRouter, innerRouter.state.location, 'replace-entries')
},
[setNavigationDataForRouter],
)

const setActiveRemixScene = useSetAtom(ActiveRemixSceneAtom)
React.useLayoutEffect(() => {
setActiveRemixScene(basePath)
}, [basePath, setActiveRemixScene])

// initialize navigation data
// Initialise navigation data. We also need to do this if the router has changed,
// so that the navigation functions use the current router
React.useLayoutEffect(() => {
if (router != null && navigationData[EP.toString(basePath)] == null) {
updateNavigationData(router, router.state.location)
if (router != null) {
initialiseNavigationData(router)
}
}, [router, navigationData, basePath, updateNavigationData])
}, [router, initialiseNavigationData])

// apply changes navigation data
React.useLayoutEffect(() => {
Expand Down