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

[DNM] Fix tab navigation with react-router >= 6.19 #9476

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 3 additions & 2 deletions packages/ra-ui-materialui/src/detail/Tab.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as React from 'react';
import { isValidElement, ReactElement, ReactNode } from 'react';
import PropTypes from 'prop-types';
import { Link, useLocation } from 'react-router-dom';
import { Link, useLocation, useResolvedPath } from 'react-router-dom';
import { Tab as MuiTab, TabProps as MuiTabProps, Stack } from '@mui/material';
import { ResponsiveStyleValue } from '@mui/system';
import { useTranslate, RaRecord } from 'ra-core';
Expand Down Expand Up @@ -71,9 +71,10 @@ export const Tab = ({
}: TabProps) => {
const translate = useTranslate();
const location = useLocation();
const resolvedPath = useResolvedPath('..');
const propsForLink = {
component: Link,
to: { ...location, pathname: value },
to: { ...location, pathname: `${resolvedPath.pathname}/${value}` },
};

const renderHeader = () => {
Expand Down
268 changes: 143 additions & 125 deletions packages/ra-ui-materialui/src/detail/TabbedShowLayout.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
import * as React from 'react';
import { MemoryRouter } from 'react-router-dom';
import { Divider as MuiDivider } from '@mui/material';
import {
RecordContextProvider,
ResourceContext,
useRecordContext,
ResourceContextProvider,
WithRecord,
testDataProvider,
useRecordContext,
} from 'ra-core';
import * as React from 'react';
import { Navigate, Route, Routes } from 'react-router-dom';
import { AdminContext } from '../AdminContext';
import { Labeled } from '../Labeled';
import { TextField, NumberField } from '../field';
import { Show } from '../detail';
import { NumberField, TextField } from '../field';
import { TabbedShowLayout } from './TabbedShowLayout';

export default { title: 'ra-ui-materialui/detail/TabbedShowLayout' };
Expand All @@ -22,47 +24,69 @@ const record = {
year: 1869,
};

const Wrapper = ({ children }) => (
<AdminContext
i18nProvider={{
translate: (x, options) => options?._ ?? x,
changeLocale: () => Promise.resolve(),
getLocale: () => 'en',
}}
dataProvider={testDataProvider({
getOne: () => Promise.resolve({ data: record }),
})}
>
<Routes>
<Route
path="/books/:id/show/*"
element={
<ResourceContextProvider value="books">
<Show id={1} sx={{ width: 600 }}>
{children}
</Show>
</ResourceContextProvider>
}
/>
<Route
path="*"
element={<Navigate to="/books/1/show" replace={true} />}
/>
</Routes>
</AdminContext>
);

export const Basic = () => (
<MemoryRouter>
<ResourceContext.Provider value="books">
<RecordContextProvider value={record}>
<TabbedShowLayout>
<TabbedShowLayout.Tab label="First">
<TextField source="id" />
<TextField source="title" />
</TabbedShowLayout.Tab>
<TabbedShowLayout.Tab label="Second">
<TextField source="author" />
<TextField source="summary" />
<NumberField source="year" />
</TabbedShowLayout.Tab>
</TabbedShowLayout>
</RecordContextProvider>
</ResourceContext.Provider>
</MemoryRouter>
<Wrapper>
<TabbedShowLayout>
<TabbedShowLayout.Tab label="First">
<TextField source="id" />
<TextField source="title" />
</TabbedShowLayout.Tab>
<TabbedShowLayout.Tab label="Second">
<TextField source="author" />
<TextField source="summary" />
<NumberField source="year" />
</TabbedShowLayout.Tab>
</TabbedShowLayout>
</Wrapper>
);

export const Count = () => (
<MemoryRouter>
<ResourceContext.Provider value="books">
<RecordContextProvider value={record}>
<TabbedShowLayout>
<TabbedShowLayout.Tab label="Main">
<TextField source="id" />
<TextField source="title" />
</TabbedShowLayout.Tab>
<TabbedShowLayout.Tab label="Details">
<TextField source="author" />
<TextField source="summary" />
<NumberField source="year" />
</TabbedShowLayout.Tab>
<TabbedShowLayout.Tab label="Reviews" count={27}>
<TextField source="reviews" />
</TabbedShowLayout.Tab>
</TabbedShowLayout>
</RecordContextProvider>
</ResourceContext.Provider>
</MemoryRouter>
<Wrapper>
<TabbedShowLayout>
<TabbedShowLayout.Tab label="Main">
<TextField source="id" />
<TextField source="title" />
</TabbedShowLayout.Tab>
<TabbedShowLayout.Tab label="Details">
<TextField source="author" />
<TextField source="summary" />
<NumberField source="year" />
</TabbedShowLayout.Tab>
<TabbedShowLayout.Tab label="Reviews" count={27}>
<TextField source="reviews" />
</TabbedShowLayout.Tab>
</TabbedShowLayout>
</Wrapper>
);

const BookTitle = () => {
Expand All @@ -71,98 +95,92 @@ const BookTitle = () => {
};

export const CustomChild = () => (
<MemoryRouter>
<ResourceContext.Provider value="books">
<RecordContextProvider value={record}>
<TabbedShowLayout>
<TabbedShowLayout.Tab label="First">
<BookTitle />
<WithRecord
render={record => <span>{record.author}</span>}
/>
</TabbedShowLayout.Tab>
</TabbedShowLayout>
</RecordContextProvider>
</ResourceContext.Provider>
</MemoryRouter>
<Wrapper>
<TabbedShowLayout>
<TabbedShowLayout.Tab label="First">
<BookTitle />
<WithRecord render={record => <span>{record.author}</span>} />
</TabbedShowLayout.Tab>
</TabbedShowLayout>
</Wrapper>
);

export const CustomLabel = () => (
<MemoryRouter>
<ResourceContext.Provider value="books">
<RecordContextProvider value={record}>
<TabbedShowLayout>
<TabbedShowLayout.Tab label="First">
<TextField label="Identifier" source="id" />
<TextField source="title" />
<Labeled label="Author name">
<TextField source="author" />
</Labeled>
<TextField label={false} source="summary" />
<NumberField source="year" />
</TabbedShowLayout.Tab>
</TabbedShowLayout>
</RecordContextProvider>
</ResourceContext.Provider>
</MemoryRouter>
<Wrapper>
<TabbedShowLayout>
<TabbedShowLayout.Tab label="First">
<TextField label="Identifier" source="id" />
<TextField source="title" />
<Labeled label="Author name">
<TextField source="author" />
</Labeled>
<TextField label={false} source="summary" />
<NumberField source="year" />
</TabbedShowLayout.Tab>
</TabbedShowLayout>
</Wrapper>
);

export const Spacing = () => (
<MemoryRouter>
<ResourceContext.Provider value="books">
<RecordContextProvider value={record}>
<TabbedShowLayout spacing={3}>
<TabbedShowLayout.Tab label="First">
<TextField source="id" />
<TextField source="title" />
<TextField source="author" />
<TextField source="summary" />
<NumberField source="year" />
</TabbedShowLayout.Tab>
</TabbedShowLayout>
</RecordContextProvider>
</ResourceContext.Provider>
</MemoryRouter>
<Wrapper>
<TabbedShowLayout spacing={3}>
<TabbedShowLayout.Tab label="First">
<TextField source="id" />
<TextField source="title" />
<TextField source="author" />
<TextField source="summary" />
<NumberField source="year" />
</TabbedShowLayout.Tab>
</TabbedShowLayout>
</Wrapper>
);

export const Divider = () => (
<MemoryRouter>
<ResourceContext.Provider value="books">
<RecordContextProvider value={record}>
<TabbedShowLayout divider={<MuiDivider />}>
<TabbedShowLayout.Tab label="First">
<TextField source="id" />
<TextField source="title" />
<TextField source="author" />
<TextField source="summary" />
<NumberField source="year" />
</TabbedShowLayout.Tab>
</TabbedShowLayout>
</RecordContextProvider>
</ResourceContext.Provider>
</MemoryRouter>
<Wrapper>
<TabbedShowLayout divider={<MuiDivider />}>
<TabbedShowLayout.Tab label="First">
<TextField source="id" />
<TextField source="title" />
<TextField source="author" />
<TextField source="summary" />
<NumberField source="year" />
</TabbedShowLayout.Tab>
</TabbedShowLayout>
</Wrapper>
);

export const SX = () => (
<MemoryRouter>
<ResourceContext.Provider value="books">
<RecordContextProvider value={record}>
<TabbedShowLayout
sx={{
margin: 2,
padding: 2,
bgcolor: 'text.disabled',
}}
>
<TabbedShowLayout.Tab label="First">
<TextField source="id" />
<TextField source="title" />
<TextField source="author" />
<TextField source="summary" />
<NumberField source="year" />
</TabbedShowLayout.Tab>
</TabbedShowLayout>
</RecordContextProvider>
</ResourceContext.Provider>
</MemoryRouter>
<Wrapper>
<TabbedShowLayout
sx={{
margin: 2,
padding: 2,
bgcolor: 'text.disabled',
}}
>
<TabbedShowLayout.Tab label="First">
<TextField source="id" />
<TextField source="title" />
<TextField source="author" />
<TextField source="summary" />
<NumberField source="year" />
</TabbedShowLayout.Tab>
</TabbedShowLayout>
</Wrapper>
);

export const WithPath = () => (
<Wrapper>
<TabbedShowLayout>
<TabbedShowLayout.Tab label="First">
<TextField source="id" />
<TextField source="title" />
</TabbedShowLayout.Tab>
<TabbedShowLayout.Tab label="Second" path="second">
<TextField source="author" />
<TextField source="summary" />
<NumberField source="year" />
</TabbedShowLayout.Tab>
</TabbedShowLayout>
</Wrapper>
);
5 changes: 3 additions & 2 deletions packages/ra-ui-materialui/src/form/FormTabHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import * as React from 'react';
import { ReactElement, ReactNode } from 'react';
import { isElement } from 'react-is';
import PropTypes from 'prop-types';
import { Link, useLocation } from 'react-router-dom';
import { Link, useLocation, useResolvedPath } from 'react-router-dom';
import { Tab as MuiTab, TabProps as MuiTabProps } from '@mui/material';
import clsx from 'clsx';
import { useTranslate, useFormGroup } from 'ra-core';
Expand All @@ -22,12 +22,13 @@ export const FormTabHeader = ({
}: FormTabHeaderProps): ReactElement => {
const translate = useTranslate();
const location = useLocation();
const resolvedPath = useResolvedPath('..');
const { isSubmitted } = useFormState();
const formGroup = useFormGroup(value.toString());

const propsForLink = {
component: Link,
to: { ...location, pathname: value },
to: { ...location, pathname: `${resolvedPath.pathname}/${value}` },
};

let tabLabel = isElement(label) ? label : translate(label, { _: label });
Expand Down