Skip to content

Commit e3845be

Browse files
layout and fixes
1 parent 9036a7c commit e3845be

22 files changed

Lines changed: 739 additions & 209 deletions

e2e/react-start/hmr/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,12 @@
1717
},
1818
"devDependencies": {
1919
"@playwright/test": "^1.50.1",
20+
"@tailwindcss/vite": "^4.2.2",
2021
"@tanstack/router-e2e-utils": "workspace:^",
2122
"@types/node": "^22.10.2",
2223
"@types/react": "^19.0.8",
2324
"@types/react-dom": "^19.0.3",
25+
"tailwindcss": "^4.2.2",
2426
"@vitejs/plugin-react": "^6.0.1",
2527
"typescript": "^5.7.2",
2628
"vite": "^8.0.0"

e2e/react-start/hmr/src/routes/__root.tsx

Lines changed: 174 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,15 @@ import {
99
useMatches,
1010
} from '@tanstack/react-router'
1111
import type { ReactNode } from 'react'
12+
import appCss from '~/styles/app.css?url'
1213

1314
export const Route = createRootRoute({
1415
head: () => ({
1516
meta: [
1617
{ charSet: 'utf-8' },
1718
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
1819
],
20+
links: [{ rel: 'stylesheet', href: appCss }],
1921
}),
2022
loader: () => ({
2123
crumb: 'Home',
@@ -30,10 +32,20 @@ function Breadcrumbs() {
3032
)
3133

3234
return (
33-
<nav>
34-
{matchesWithCrumbs.map((match) => (
35-
<span data-testid={`crumb-${match.routeId}`} key={match.id}>
36-
{match.loaderData?.crumb}
35+
<nav className="flex flex-wrap items-center gap-2">
36+
{matchesWithCrumbs.map((match, index) => (
37+
<span className="flex items-center gap-2" key={match.id}>
38+
{index > 0 ? (
39+
<span className="text-slate-400" aria-hidden>
40+
/
41+
</span>
42+
) : null}
43+
<span
44+
className="rounded-full bg-[rgba(31,111,120,0.10)] px-3 py-1 text-sm font-semibold text-[var(--color-lagoon)]"
45+
data-testid={`crumb-${match.routeId}`}
46+
>
47+
{match.loaderData?.crumb}
48+
</span>
3749
</span>
3850
))}
3951
</nav>
@@ -60,8 +72,16 @@ function RootDocument({
6072
<head>
6173
<HeadContent />
6274
</head>
63-
<body>
64-
<p data-testid="root-component-marker">{marker}</p>
75+
<body className="hmr-shell">
76+
<div className="hidden" aria-hidden="true">
77+
<p data-testid="root-component-marker">{marker}</p>
78+
</div>
79+
<div className="pointer-events-none fixed right-4 top-4 z-20 hidden sm:block">
80+
<div className="rounded-full border border-white/70 bg-white/75 px-4 py-2 shadow-lg backdrop-blur">
81+
<span className="hmr-label">Current marker </span>
82+
<span className="hmr-kbd">{marker}</span>
83+
</div>
84+
</div>
6585
{children}
6686
<Scripts />
6787
</body>
@@ -81,9 +101,11 @@ function RootShellDocument({
81101
<head>
82102
<HeadContent />
83103
</head>
84-
<body>
85-
<div data-testid="root-shell">
104+
<body className="hmr-shell">
105+
<div className="hidden" aria-hidden="true">
86106
<p data-testid="root-shell-marker">{marker}</p>
107+
</div>
108+
<div className="hmr-shell" data-testid="root-shell">
87109
{children}
88110
</div>
89111
<Scripts />
@@ -93,62 +115,151 @@ function RootShellDocument({
93115
}
94116

95117
function RootContent() {
118+
const navLinks = [
119+
{
120+
testId: 'home-link',
121+
to: '/',
122+
label: 'Home',
123+
detail: 'Local state + route option HMR',
124+
},
125+
{
126+
testId: 'child-link',
127+
to: '/child',
128+
label: 'Child',
129+
detail: 'Loader and beforeLoad updates',
130+
},
131+
{
132+
testId: 'inputs-link',
133+
to: '/inputs',
134+
label: 'Inputs',
135+
detail: 'Uncontrolled input preservation',
136+
},
137+
{
138+
testId: 'component-hmr-link',
139+
to: '/component-hmr',
140+
label: 'Component HMR',
141+
detail: 'Base component refresh checks',
142+
},
143+
{
144+
testId: 'component-hmr-inline-split-link',
145+
to: '/component-hmr-inline-split',
146+
label: 'Inline Split',
147+
detail: 'Inline component with default splitting',
148+
},
149+
{
150+
testId: 'component-hmr-inline-nosplit-link',
151+
to: '/component-hmr-inline-nosplit',
152+
label: 'Inline No Split',
153+
detail: 'Inline component with code splitting disabled',
154+
},
155+
{
156+
testId: 'component-hmr-named-split-link',
157+
to: '/component-hmr-named-split',
158+
label: 'Named Split',
159+
detail: 'Named component with default splitting',
160+
},
161+
{
162+
testId: 'component-hmr-named-nosplit-link',
163+
to: '/component-hmr-named-nosplit',
164+
label: 'Named No Split',
165+
detail: 'Named component with code splitting disabled',
166+
},
167+
{
168+
testId: 'component-hmr-inline-error-split-link',
169+
to: '/component-hmr-inline-error-split',
170+
label: 'Inline Error Split',
171+
detail: 'Inline component with only errorComponent split',
172+
},
173+
{
174+
testId: 'component-hmr-named-error-split-link',
175+
to: '/component-hmr-named-error-split',
176+
label: 'Named Error Split',
177+
detail: 'Named component with only errorComponent split',
178+
},
179+
] as const
180+
96181
return (
97-
<>
98-
<Link data-testid="home-link" to="/">
99-
Home
100-
</Link>
101-
<Link data-testid="child-link" to="/child">
102-
Child
103-
</Link>
104-
<Link data-testid="inputs-link" to="/inputs">
105-
Inputs
106-
</Link>
107-
<Link data-testid="component-hmr-link" to="/component-hmr">
108-
Component HMR
109-
</Link>
110-
<Link
111-
data-testid="component-hmr-inline-split-link"
112-
to="/component-hmr-inline-split"
113-
>
114-
Component HMR Inline Split
115-
</Link>
116-
<Link
117-
data-testid="component-hmr-inline-nosplit-link"
118-
to="/component-hmr-inline-nosplit"
119-
>
120-
Component HMR Inline No Split
121-
</Link>
122-
<Link
123-
data-testid="component-hmr-named-split-link"
124-
to="/component-hmr-named-split"
125-
>
126-
Component HMR Named Split
127-
</Link>
128-
<Link
129-
data-testid="component-hmr-named-nosplit-link"
130-
to="/component-hmr-named-nosplit"
131-
>
132-
Component HMR Named No Split
133-
</Link>
134-
<Link
135-
data-testid="component-hmr-inline-error-split-link"
136-
to="/component-hmr-inline-error-split"
137-
>
138-
Component HMR Inline Error Split
139-
</Link>
140-
<Link
141-
data-testid="component-hmr-named-error-split-link"
142-
to="/component-hmr-named-error-split"
143-
>
144-
Component HMR Named Error Split
145-
</Link>
146-
<input data-testid="root-message" defaultValue="root state" />
147-
<Breadcrumbs />
182+
<div className="hmr-page">
183+
<header className="hmr-hero">
184+
<div className="relative z-10 flex flex-col gap-6">
185+
<div className="flex flex-wrap items-start justify-between gap-4">
186+
<div className="max-w-3xl space-y-3">
187+
<p className="hmr-label">React Start HMR Playground</p>
188+
<h1 className="font-display text-4xl font-bold tracking-tight text-[var(--color-night)] sm:text-5xl">
189+
Route refresh behavior
190+
</h1>
191+
<p className="max-w-2xl text-sm leading-6 text-slate-600 sm:text-base">
192+
This sandbox exercises component state preservation, route
193+
option updates, stale data clearing, and split-route refresh
194+
behavior.
195+
</p>
196+
</div>
197+
</div>
198+
199+
<div className="grid gap-4 lg:grid-cols-[1.3fr_0.7fr]">
200+
<div className="space-y-3">
201+
<div className="flex items-center justify-between gap-4">
202+
<span className="hmr-label">Route matrix</span>
203+
<span className="text-xs font-medium text-slate-500">
204+
Stable test ids preserved
205+
</span>
206+
</div>
207+
<div className="hmr-nav-grid">
208+
{navLinks.map((link) => (
209+
<Link
210+
activeProps={{
211+
className:
212+
'group hmr-nav-link border-[var(--color-reef)] bg-white shadow-md',
213+
}}
214+
className="group hmr-nav-link"
215+
data-testid={link.testId}
216+
key={link.testId}
217+
to={link.to}
218+
>
219+
<span className="font-display text-lg font-semibold text-[var(--color-night)]">
220+
{link.label}
221+
</span>
222+
<span className="text-sm leading-5 text-slate-600 group-hover:text-slate-700">
223+
{link.detail}
224+
</span>
225+
</Link>
226+
))}
227+
</div>
228+
</div>
229+
230+
<div className="hmr-card flex flex-col gap-4">
231+
<div>
232+
<p className="hmr-label">Root state</p>
233+
<p className="mt-2 text-sm leading-6 text-slate-600">
234+
This uncontrolled input is used by the tests to verify
235+
root-level state survives route and component HMR updates.
236+
</p>
237+
</div>
238+
<input
239+
className="hmr-input"
240+
data-testid="root-message"
241+
defaultValue="root state"
242+
/>
243+
<div className="space-y-2">
244+
<p className="hmr-label">Breadcrumbs</p>
245+
<Breadcrumbs />
246+
</div>
247+
<ClientOnly>
248+
<p
249+
className="inline-flex w-fit items-center rounded-full bg-emerald-100 px-3 py-1 text-sm font-semibold text-emerald-700"
250+
data-testid="hydrated"
251+
>
252+
hydrated
253+
</p>
254+
</ClientOnly>
255+
</div>
256+
</div>
257+
</div>
258+
</header>
259+
260+
<div className="pb-8">
148261
<Outlet />
149-
<ClientOnly>
150-
<p data-testid="hydrated">hydrated</p>
151-
</ClientOnly>
152-
</>
262+
</div>
263+
</div>
153264
)
154265
}

e2e/react-start/hmr/src/routes/child.tsx

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,30 @@ export const Route = createFileRoute('/child')({
1313
function Child() {
1414
const context = Route.useRouteContext()
1515
return (
16-
<div>
17-
<p data-testid="child">child</p>
16+
<main className="hmr-card flex flex-col gap-4">
17+
<div className="flex items-center justify-between gap-4">
18+
<div>
19+
<p className="hmr-label">Child route</p>
20+
<p
21+
className="mt-2 text-2xl font-bold text-[var(--color-night)]"
22+
data-testid="child"
23+
>
24+
child
25+
</p>
26+
</div>
27+
<span className="hmr-kbd">loader + beforeLoad</span>
28+
</div>
1829
{context.greeting ? (
19-
<p data-testid="child-greeting">{context.greeting}</p>
30+
<div className="hmr-stat">
31+
<p className="hmr-label">Greeting</p>
32+
<p
33+
className="mt-2 text-lg font-semibold text-[var(--color-night)]"
34+
data-testid="child-greeting"
35+
>
36+
{context.greeting}
37+
</p>
38+
</div>
2039
) : null}
21-
</div>
40+
</main>
2241
)
2342
}

0 commit comments

Comments
 (0)