Skip to content

Commit 99d2b5b

Browse files
authored
Merge pull request #11138 from marmelab/fix-most-specific-matching-route
Fix the most specific matching route when using pathless routes
2 parents 02400fb + 3a8d27e commit 99d2b5b

3 files changed

Lines changed: 454 additions & 108 deletions

File tree

packages/ra-router-tanstack/src/tanStackRouterProvider.spec.tsx

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ import {
2525
QueryParameters,
2626
PathlessLayoutRoutes,
2727
NestedResourcesPrecedence,
28+
PathlessLayoutRoutesPriority,
29+
PathlessLayoutRoutesWithEmptyRoute,
30+
PathlessLayoutRoutesWithIndexRoute,
2831
} from './tanStackRouterProvider.stories';
2932
import { tanStackRouterProvider } from './tanStackRouterProvider';
3033

@@ -1479,6 +1482,62 @@ describe('tanStackRouterProvider', () => {
14791482
expect(screen.getByTestId('comments-page')).toBeInTheDocument();
14801483
});
14811484
});
1485+
1486+
it('should match the most specific layout route within pathless layout routes', async () => {
1487+
window.location.hash = '#/posts';
1488+
1489+
render(<PathlessLayoutRoutesPriority />);
1490+
1491+
await waitFor(() => {
1492+
expect(screen.getByTestId('posts-page')).toBeInTheDocument();
1493+
});
1494+
1495+
fireEvent.click(screen.getByText('User'));
1496+
1497+
await waitFor(() => {
1498+
expect(screen.getByTestId('users-page')).toBeInTheDocument();
1499+
});
1500+
1501+
fireEvent.click(screen.getByText('Block a user'));
1502+
1503+
await waitFor(() => {
1504+
expect(
1505+
screen.getByTestId('block-user-page')
1506+
).toBeInTheDocument();
1507+
});
1508+
});
1509+
});
1510+
1511+
it('should match the empty path route as most specific within pathless layout routes', async () => {
1512+
window.location.hash = '#/posts';
1513+
1514+
render(<PathlessLayoutRoutesWithEmptyRoute />);
1515+
1516+
await waitFor(() => {
1517+
expect(screen.getByTestId('posts-page')).toBeInTheDocument();
1518+
});
1519+
1520+
fireEvent.click(screen.getByText('Home (path="")'));
1521+
1522+
await waitFor(() => {
1523+
expect(screen.getByTestId('home-page')).toBeInTheDocument();
1524+
});
1525+
});
1526+
1527+
it('should match the index route as most specific within pathless layout routes', async () => {
1528+
window.location.hash = '#/posts';
1529+
1530+
render(<PathlessLayoutRoutesWithIndexRoute />);
1531+
1532+
await waitFor(() => {
1533+
expect(screen.getByTestId('posts-page')).toBeInTheDocument();
1534+
});
1535+
1536+
fireEvent.click(screen.getByText('Home (index)'));
1537+
1538+
await waitFor(() => {
1539+
expect(screen.getByTestId('home-page')).toBeInTheDocument();
1540+
});
14821541
});
14831542

14841543
describe('Resource Children (Route as children of Resource)', () => {

packages/ra-router-tanstack/src/tanStackRouterProvider.stories.tsx

Lines changed: 265 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1481,3 +1481,268 @@ export const PathlessLayoutRoutes = () => {
14811481
</RouterProviderContext.Provider>
14821482
);
14831483
};
1484+
1485+
export const PathlessLayoutRoutesPriority = () => {
1486+
const { RouterWrapper } = tanStackRouterProvider;
1487+
1488+
return (
1489+
<RouterProviderContext.Provider value={tanStackRouterProvider}>
1490+
<RouterWrapper>
1491+
<div data-testid="layout-wrapper">
1492+
<nav>
1493+
<LinkBase to="/posts" style={{ marginRight: 10 }}>
1494+
Posts
1495+
</LinkBase>
1496+
<LinkBase to="/comments" style={{ marginRight: 10 }}>
1497+
Comments
1498+
</LinkBase>
1499+
<LinkBase
1500+
to="/users/john_doe"
1501+
style={{ marginRight: 10 }}
1502+
>
1503+
User
1504+
</LinkBase>
1505+
<LinkBase
1506+
to="/users/jane_doe/block"
1507+
style={{ marginRight: 10 }}
1508+
>
1509+
Block a user
1510+
</LinkBase>
1511+
</nav>
1512+
<div
1513+
style={{
1514+
border: '2px solid blue',
1515+
padding: 20,
1516+
marginTop: 10,
1517+
}}
1518+
>
1519+
<Routes>
1520+
<Route
1521+
path="/posts"
1522+
element={
1523+
<div data-testid="posts-page">
1524+
Posts Page
1525+
</div>
1526+
}
1527+
/>
1528+
<Route
1529+
path="/comments"
1530+
element={
1531+
<div data-testid="comments-page">
1532+
Comments Page
1533+
</div>
1534+
}
1535+
/>
1536+
<Route
1537+
element={
1538+
<div
1539+
style={{
1540+
border: '2px solid green',
1541+
padding: 20,
1542+
marginTop: 10,
1543+
}}
1544+
>
1545+
<RouterOutlet />
1546+
</div>
1547+
}
1548+
>
1549+
<Route
1550+
path="/users/*"
1551+
element={
1552+
<div data-testid="users-page">
1553+
Users View
1554+
</div>
1555+
}
1556+
/>
1557+
</Route>
1558+
<Route
1559+
element={
1560+
<div
1561+
style={{
1562+
border: '2px solid red',
1563+
padding: 20,
1564+
marginTop: 10,
1565+
}}
1566+
>
1567+
<RouterOutlet />
1568+
</div>
1569+
}
1570+
>
1571+
<Route
1572+
path="/users/:username/block"
1573+
element={
1574+
<div data-testid="block-user-page">
1575+
Block a user
1576+
</div>
1577+
}
1578+
/>
1579+
</Route>
1580+
</Routes>
1581+
</div>
1582+
</div>
1583+
<LocationDisplay />
1584+
</RouterWrapper>
1585+
</RouterProviderContext.Provider>
1586+
);
1587+
};
1588+
1589+
export const PathlessLayoutRoutesWithEmptyRoute = () => {
1590+
const { RouterWrapper } = tanStackRouterProvider;
1591+
1592+
return (
1593+
<RouterProviderContext.Provider value={tanStackRouterProvider}>
1594+
<RouterWrapper>
1595+
<p style={{ marginBottom: 10 }}>
1596+
Expected: "/" renders Home Page (path=""). If you see
1597+
Catch-all Page instead, path="" is being treated as
1598+
catch-all.
1599+
</p>
1600+
<Routes>
1601+
<Route
1602+
path="*"
1603+
element={
1604+
<div data-testid="catchall-page">
1605+
Catch-all Page
1606+
</div>
1607+
}
1608+
/>
1609+
<Route
1610+
element={
1611+
<div data-testid="layout-wrapper">
1612+
<h2>Layout Wrapper</h2>
1613+
<nav>
1614+
<LinkBase
1615+
to="/posts"
1616+
style={{ marginRight: 10 }}
1617+
>
1618+
Posts
1619+
</LinkBase>
1620+
<LinkBase to="/comments">Comments</LinkBase>
1621+
</nav>
1622+
<nav>
1623+
<LinkBase
1624+
to="/"
1625+
style={{ marginRight: 10 }}
1626+
>
1627+
Home (path="")
1628+
</LinkBase>
1629+
</nav>
1630+
<div
1631+
style={{
1632+
border: '2px solid blue',
1633+
padding: 20,
1634+
marginTop: 10,
1635+
}}
1636+
>
1637+
<RouterOutlet />
1638+
</div>
1639+
</div>
1640+
}
1641+
>
1642+
<Route
1643+
path=""
1644+
element={
1645+
<div data-testid="home-page">
1646+
Home Page (path="")
1647+
</div>
1648+
}
1649+
/>
1650+
<Route
1651+
path="/posts"
1652+
element={
1653+
<div data-testid="posts-page">Posts Page</div>
1654+
}
1655+
/>
1656+
<Route
1657+
path="/comments"
1658+
element={
1659+
<div data-testid="comments-page">
1660+
Comments Page
1661+
</div>
1662+
}
1663+
/>
1664+
</Route>
1665+
</Routes>
1666+
<LocationDisplay />
1667+
</RouterWrapper>
1668+
</RouterProviderContext.Provider>
1669+
);
1670+
};
1671+
1672+
export const PathlessLayoutRoutesWithIndexRoute = () => {
1673+
const { RouterWrapper } = tanStackRouterProvider;
1674+
1675+
return (
1676+
<RouterProviderContext.Provider value={tanStackRouterProvider}>
1677+
<RouterWrapper>
1678+
<Routes>
1679+
<Route
1680+
path="*"
1681+
element={
1682+
<div data-testid="catchall-page">
1683+
Catch-all Page
1684+
</div>
1685+
}
1686+
/>
1687+
<Route
1688+
element={
1689+
<div data-testid="layout-wrapper">
1690+
<h2>Layout Wrapper</h2>
1691+
<nav>
1692+
<LinkBase
1693+
to="/posts"
1694+
style={{ marginRight: 10 }}
1695+
>
1696+
Posts
1697+
</LinkBase>
1698+
<LinkBase to="/comments">Comments</LinkBase>
1699+
</nav>
1700+
<nav>
1701+
<LinkBase
1702+
to="/"
1703+
style={{ marginRight: 10 }}
1704+
>
1705+
Home (index)
1706+
</LinkBase>
1707+
</nav>
1708+
<div
1709+
style={{
1710+
border: '2px solid blue',
1711+
padding: 20,
1712+
marginTop: 10,
1713+
}}
1714+
>
1715+
<RouterOutlet />
1716+
</div>
1717+
</div>
1718+
}
1719+
>
1720+
<Route
1721+
index
1722+
element={
1723+
<div data-testid="home-page">
1724+
Home Page (index)
1725+
</div>
1726+
}
1727+
/>
1728+
<Route
1729+
path="/posts"
1730+
element={
1731+
<div data-testid="posts-page">Posts Page</div>
1732+
}
1733+
/>
1734+
<Route
1735+
path="/comments"
1736+
element={
1737+
<div data-testid="comments-page">
1738+
Comments Page
1739+
</div>
1740+
}
1741+
/>
1742+
</Route>
1743+
</Routes>
1744+
<LocationDisplay />
1745+
</RouterWrapper>
1746+
</RouterProviderContext.Provider>
1747+
);
1748+
};

0 commit comments

Comments
 (0)