-
Notifications
You must be signed in to change notification settings - Fork 38
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
feat(next-app-router) implement an adapter for next-app-router #75
Open
XionWCFM
wants to merge
20
commits into
toss:main
Choose a base branch
from
XionWCFM:next-app-router
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 17 commits
Commits
Show all changes
20 commits
Select commit
Hold shift + click to select a range
d890bbf
:sparkles: Apps/approuter add
XionWCFM b25a6b9
Merge remote-tracking branch 'origin'
XionWCFM aff6f94
:sparkles: Apps/pagesrouter add
XionWCFM c91a77d
:construction: App router funnel add
XionWCFM f889afa
:sparkles: App router e2e test add
XionWCFM 52df74e
:sparkles: Pages router e2e add
XionWCFM 0453ae1
chore: folder name change apps -> examples | nextjs prefix add
XionWCFM d9144d2
chore: remove package.lock.json & add pnpm.lock
XionWCFM 4e7a8a6
Merge branch 'toss:main' into main
XionWCFM 6ad8f08
feat: @use-funnel/next-app-router
XionWCFM 6fe5a4d
test: add overlay test case
XionWCFM f32ddfe
chore: history state type change any to record type
XionWCFM e614027
chore: funnel component name change
XionWCFM 8ef361b
chore: app router build error fix
XionWCFM 0743d61
Update packages/next-app-router/package.json
XionWCFM 9cf1cad
Update packages/next-app-router/src/index.ts
XionWCFM f2f5e23
Update packages/next-app-router/src/index.ts
XionWCFM f049bc1
fix: remove window access inside setState
XionWCFM 339b632
fix: remove state dependency
XionWCFM 8f6743c
chore: remove side effects inside setState
XionWCFM File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
import { Suspense } from 'react'; | ||
import { OverlayFunnel } from '../../src/overlay/OverlayCaseFunnel'; | ||
|
||
export default function Page() { | ||
return ( | ||
<Suspense> | ||
<OverlayFunnel />; | ||
</Suspense> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,10 @@ | ||
'use client'; | ||
import dynamic from 'next/dynamic'; | ||
const TestAppRouterFunnel = dynamic(() => | ||
import('../src/funnel').then(({ TestAppRouterFunnel }) => TestAppRouterFunnel), | ||
); | ||
import { Suspense } from 'react'; | ||
import { TestAppRouterFunnel } from '~/src/funnel'; | ||
|
||
export default function Home() { | ||
//A pre-render error occurs in @use-funnel/browser 0.0.5 version. | ||
return <TestAppRouterFunnel />; | ||
return ( | ||
<Suspense> | ||
<TestAppRouterFunnel /> | ||
</Suspense> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
33 changes: 33 additions & 0 deletions
33
examples/nextjs-app-router/src/overlay/OverlayCaseFunnel.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
'use client'; | ||
|
||
import { useFunnel } from '@use-funnel/next-app-router'; | ||
import { SchoolInput } from './SchoolInput'; | ||
import { StartDate } from './StartDate'; | ||
|
||
export const OverlayFunnel = () => { | ||
const funnel = useFunnel<{ | ||
SelectSchool: { school?: string }; | ||
StartDate: { school: string; startDate?: string }; | ||
Confirm: { school: string; startDate: string }; | ||
}>({ id: 'general', initial: { context: {}, step: 'SelectSchool' } }); | ||
|
||
return ( | ||
<funnel.Render | ||
SelectSchool={({ history }) => <SchoolInput onNext={(school) => history.push('StartDate', { school: school })} />} | ||
StartDate={funnel.Render.overlay({ | ||
render: ({ history, context }) => ( | ||
<StartDate | ||
startDate={context.startDate} | ||
onNext={(startDate) => history.push('Confirm', { school: context.school, startDate: startDate })} | ||
/> | ||
), | ||
})} | ||
Confirm={({ context }) => ( | ||
<div> | ||
<div>school: {context.school}</div> | ||
<div>startDate: {context.startDate}</div> | ||
</div> | ||
)} | ||
/> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
import { useState } from 'react'; | ||
|
||
interface Props { | ||
onNext: (school: string) => void; | ||
} | ||
|
||
export function SchoolInput({ onNext }: Props) { | ||
const [school, setSchool] = useState('A'); | ||
return ( | ||
<div> | ||
<h2>Select Your School</h2> | ||
<input type="radio" value={'A'} checked={school === 'A'} onChange={(e) => setSchool(e.target.value)} /> | ||
<input type="radio" value={'B'} checked={school === 'B'} onChange={(e) => setSchool(e.target.value)} /> | ||
<input type="radio" value={'C'} checked={school === 'C'} onChange={(e) => setSchool(e.target.value)} /> | ||
<button onClick={() => onNext(school)}>school next</button> | ||
</div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import { ReactNode, useState } from 'react'; | ||
|
||
export const StartDate = ({ startDate, onNext }: { startDate?: string; onNext: (startDate: string) => void }) => { | ||
const [date, setDate] = useState(startDate ?? ''); | ||
|
||
return ( | ||
<div> | ||
<input type="date" value={date} onChange={(e) => setDate(e.target.value)} /> | ||
<button onClick={() => onNext(date)}>overlay next</button> | ||
</div> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
'use client'; | ||
import { OverlayProvider } from 'overlay-kit'; | ||
export const Providers = ({ children }: { children: React.ReactNode }) => { | ||
return <OverlayProvider>{children}</OverlayProvider>; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
{ | ||
"name": "@use-funnel/next-app-router", | ||
"version": "0.0.0", | ||
"description": "", | ||
"type": "module", | ||
"main": "./dist/index.js", | ||
"publishConfig": { | ||
"access": "public", | ||
"main": "./dist/index.js", | ||
"types": "./dist/index.d.ts", | ||
"module": "./dist/index.js", | ||
"exports": { | ||
".": { | ||
"types": "./dist/index.d.ts", | ||
"default": "./dist/index.js" | ||
}, | ||
"./package.json": "./package.json" | ||
} | ||
}, | ||
"files": [ | ||
"dist", | ||
"package.json" | ||
], | ||
"scripts": { | ||
"test": "vitest run", | ||
"test:unit": "vitest --root test/", | ||
"build": "rimraf dist && concurrently \"pnpm:build:*\"", | ||
"build:dist": "tsup", | ||
"build:types": "tsc -p tsconfig.build.json --emitDeclarationOnly", | ||
"prepublish": "pnpm test && pnpm build" | ||
}, | ||
"keywords": [], | ||
"author": "", | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/toss/use-funnel.git", | ||
"directory": "packages/next-app-router" | ||
}, | ||
"license": "MIT", | ||
"homepage": "https://use-funnel.slash.page/", | ||
"bugs": "https://github.com/toss/use-funnel/issues", | ||
"dependencies": { | ||
"@use-funnel/core": "workspace:^" | ||
}, | ||
"devDependencies": { | ||
"@testing-library/react": "^15.0.7", | ||
"@testing-library/user-event": "^14.5.2", | ||
"@types/react": "^18.3.2", | ||
"@types/react-dom": "^18.3.0", | ||
"concurrently": "^8.2.2", | ||
"globals": "^15.3.0", | ||
"jsdom": "^24.1.0", | ||
"react": "^18.3.1", | ||
"react-dom": "^18.3.1", | ||
"rimraf": "^5.0.7", | ||
"tsup": "^8.0.2", | ||
"typescript": "^5.1.6", | ||
"vitest": "^1.6.0" | ||
}, | ||
"peerDependencies": { | ||
"next": ">=13", | ||
"react": ">=18.2" | ||
}, | ||
"sideEffects": false | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
'use client'; | ||
import { createUseFunnel } from '@use-funnel/core'; | ||
import { useSearchParams } from 'next/navigation'; | ||
import { useLayoutEffect, useMemo, useState } from 'react'; | ||
|
||
export const useFunnel = createUseFunnel(({ id, initialState }) => { | ||
const searchParams = useSearchParams(); | ||
const [state, setState] = useState<Record<string, any>>({}); | ||
useLayoutEffect(() => { | ||
if (typeof window !== 'undefined') { | ||
setState(window.history.state); | ||
} | ||
|
||
function handlePopState(event: PopStateEvent) { | ||
setState(event.state); | ||
} | ||
window.addEventListener('popstate', handlePopState); | ||
return () => { | ||
window.removeEventListener('popstate', handlePopState); | ||
}; | ||
}, []); | ||
|
||
const currentStep = searchParams.get(`${id}.step`); | ||
const currentContext = state?.[`${id}.context`]; | ||
|
||
const currentState = useMemo(() => { | ||
return currentStep != null && currentContext != null | ||
? ({ | ||
step: currentStep, | ||
context: currentContext, | ||
} as typeof initialState) | ||
: initialState; | ||
}, [currentStep, currentContext, initialState]); | ||
|
||
const history: (typeof initialState)[] = useMemo( | ||
() => state?.[`${id}.histories`] ?? [currentState], | ||
[state, currentState], | ||
); | ||
|
||
const currentIndex = history.length - 1; | ||
return useMemo( | ||
() => ({ | ||
history, | ||
currentIndex, | ||
currentState, | ||
push(newState) { | ||
const newSearchParams = new URLSearchParams(searchParams); | ||
newSearchParams.set(`${id}.step`, newState.step); | ||
window.history.pushState(newHistoryState, '', `?${newSearchParams.toString()}`); | ||
setState(prevHistoryState => ({ | ||
...prevHistoryState, | ||
[`${id}.context`]: newState.context, | ||
[`${id}.histories`]: [...(history ?? []), newState], | ||
})); | ||
}, | ||
replace(newState) { | ||
const newSearchParams = new URLSearchParams(searchParams); | ||
newSearchParams.set(`${id}.step`, newState.step); | ||
const newHistoryState = { | ||
...state, | ||
[`${id}.context`]: newState.context, | ||
[`${id}.histories`]: [...(history ?? []), newState], | ||
}; | ||
window.history.replaceState(newHistoryState, '', `?${newSearchParams.toString()}`); | ||
setState(newHistoryState); | ||
}, | ||
go(index) { | ||
window.history.go(index); | ||
}, | ||
}), | ||
[history, currentIndex, currentState, searchParams, id], | ||
); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
import { render, screen } from '@testing-library/react'; | ||
import { userEvent } from '@testing-library/user-event'; | ||
import { describe, expect, test } from 'vitest'; | ||
|
||
import { useFunnel } from '../src/index.js'; | ||
|
||
describe('Test useFunnel next-app-router router', () => { | ||
test('should work', async () => { | ||
function FunnelTest() { | ||
const funnel = useFunnel<{ | ||
A: { id?: string }; | ||
B: { id: string }; | ||
}>({ | ||
id: 'vitest', | ||
initial: { | ||
step: 'A', | ||
context: {}, | ||
}, | ||
}); | ||
switch (funnel.step) { | ||
case 'A': { | ||
return <button onClick={() => funnel.history.push('B', { id: 'vitest' })}>Go B</button>; | ||
} | ||
case 'B': { | ||
return ( | ||
<div> | ||
<button onClick={() => window.history.back()}>Go Back</button> | ||
<div>{funnel.context.id}</div> | ||
</div> | ||
); | ||
} | ||
default: { | ||
throw new Error('Invalid step'); | ||
} | ||
} | ||
} | ||
|
||
render(<FunnelTest />); | ||
|
||
expect(screen.queryByText('Go B')).not.toBeNull(); | ||
|
||
const user = userEvent.setup(); | ||
await user.click(screen.getByText('Go B')); | ||
|
||
expect(screen.queryByText('vitest')).not.toBeNull(); | ||
await user.click(screen.getByText('Go Back')); | ||
|
||
expect(screen.queryByText('vitest')).toBeNull(); | ||
expect(screen.queryByText('Go B')).not.toBeNull(); | ||
}); | ||
|
||
test('hello' , async () => { | ||
|
||
}) | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
{ | ||
"extends": "./tsconfig.json", | ||
"exclude": ["test"] | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Currently, the recommended router for Next.js is App Router, so please give priority to App Router.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@use-funnel/next is already used for pages router.
If place the funnel for the app router in @use-funnel/next and change the implementation of the pages router to @use-funnel/next/page, it will be a Breaking Change.
A major update is required. I think the maintainer’s opinion is important in this. @minuukang
Or we can access it like next-mdx-remote
They also have similar problems, so they provide packages in this form.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking at my review, I think it might be easy for XionWCFM to misunderstand. I am a member of Toss, but I am not the maintainer of use-funnel! I apologize for any misunderstanding! @XionWCFM @minuukang @SunYoungKwon
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is just my personal opinion and has nothing to do with Toss as a whole or use-funnel. The maintainers of use-funnel are @minuukang and @SunYoungKwon.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for your careful consideration. And I think your opinion is good. 🙂 @manudeli