Skip to content

Commit

Permalink
Fix TabbedForm and TabbedShowLayout with react-router v7
Browse files Browse the repository at this point in the history
  • Loading branch information
slax57 committed Jan 24, 2025
1 parent 523a88f commit 67b2001
Show file tree
Hide file tree
Showing 5 changed files with 44 additions and 16 deletions.
1 change: 1 addition & 0 deletions packages/ra-core/src/routing/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ export * from './useScrollToTop';
export * from './useRestoreScrollPosition';
export * from './types';
export * from './TestMemoryRouter';
export * from './useSplatPathBase';
24 changes: 24 additions & 0 deletions packages/ra-core/src/routing/useSplatPathBase.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { useLocation, useParams } from 'react-router-dom';

/**
* Utility hook to get the base path of a splat path.
* Compatible both with react-router v6 and v7.
*
* Example:
* If a splat path is defined as `/posts/:id/show/*`,
* and the current location is `/posts/12/show/3`,
* this hook will return `/posts/12/show`.
*
* Solution inspired by
* https://github.com/remix-run/react-router/issues/11052#issuecomment-1828470203
*/
export const useSplatPathBase = () => {
const location = useLocation();
const params = useParams();
const splatPathRelativePart = params['*'];
const splatPathBase = location.pathname.replace(
new RegExp(`/${splatPathRelativePart}$`),
''
);
return splatPathBase;
};
9 changes: 7 additions & 2 deletions packages/ra-ui-materialui/src/detail/Tab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
styled,
} from '@mui/material';
import { ResponsiveStyleValue } from '@mui/system';
import { useTranslate, RaRecord } from 'ra-core';
import { useTranslate, RaRecord, useSplatPathBase } from 'ra-core';
import clsx from 'clsx';

import { Labeled } from '../Labeled';
Expand Down Expand Up @@ -76,9 +76,14 @@ export const Tab = ({
}: TabProps) => {
const translate = useTranslate();
const location = useLocation();
const splatPathBase = useSplatPathBase();
const newPathName =
value == null || value === ''
? splatPathBase
: `${splatPathBase}/${value}`;
const propsForLink = {
component: Link,
to: { ...location, pathname: value },
to: { ...location, pathname: newPathName },
};

const renderHeader = () => {
Expand Down
12 changes: 8 additions & 4 deletions packages/ra-ui-materialui/src/form/FormTabHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { ReactElement, ReactNode } from 'react';
import { Link, useLocation } from 'react-router-dom';
import { Tab as MuiTab, TabProps as MuiTabProps } from '@mui/material';
import clsx from 'clsx';
import { useTranslate, useFormGroup } from 'ra-core';
import { useTranslate, useFormGroup, useSplatPathBase } from 'ra-core';

import { TabbedFormClasses } from './TabbedFormView';

Expand All @@ -18,12 +18,16 @@ export const FormTabHeader = ({
...rest
}: FormTabHeaderProps): ReactElement => {
const translate = useTranslate();
const location = useLocation();
const formGroup = useFormGroup(value.toString());

const location = useLocation();
const splatPathBase = useSplatPathBase();
const newPathName =
value == null || value === ''
? splatPathBase
: `${splatPathBase}/${value}`;
const propsForLink = {
component: Link,
to: { ...location, pathname: value },
to: { ...location, pathname: newPathName },
};

let tabLabel =
Expand Down
14 changes: 4 additions & 10 deletions packages/ra-ui-materialui/src/form/TabbedFormView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,10 @@ import {
useState,
} from 'react';
import clsx from 'clsx';
import {
Routes,
Route,
matchPath,
useResolvedPath,
useLocation,
} from 'react-router-dom';
import { Routes, Route, matchPath, useLocation } from 'react-router-dom';
import { CardContent, Divider, SxProps } from '@mui/material';
import { styled } from '@mui/material/styles';
import { useResourceContext } from 'ra-core';
import { useResourceContext, useSplatPathBase } from 'ra-core';
import { Toolbar } from './Toolbar';
import { TabbedFormTabs, getTabbedFormTabFullPath } from './TabbedFormTabs';

Expand All @@ -35,9 +29,9 @@ export const TabbedFormView = (props: TabbedFormViewProps): ReactElement => {
...rest
} = props;
const location = useLocation();
const resolvedPath = useResolvedPath('');
const resource = useResourceContext(props);
const [tabValue, setTabValue] = useState(0);
const splatPathBase = useSplatPathBase();

const handleTabChange = (event: ChangeEvent<{}>, value: any): void => {
if (!syncWithLocation) {
Expand Down Expand Up @@ -82,7 +76,7 @@ export const TabbedFormView = (props: TabbedFormViewProps): ReactElement => {
const tabPath = getTabbedFormTabFullPath(tab, index);
const hidden = syncWithLocation
? !matchPath(
`${resolvedPath.pathname}/${tabPath}`,
`${splatPathBase}/${tabPath}`,
// The current location might have encoded segments (e.g. the record id) but resolvedPath.pathname doesn't
// and the match would fail.
getDecodedPathname(location.pathname)
Expand Down

0 comments on commit 67b2001

Please sign in to comment.