Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
4ce5679
update show test to display the datagrid
erwanMarmelab May 20, 2025
b8c3940
hide ts error in tests
erwanMarmelab May 20, 2025
26acc5d
Update ListGuesser with DataTable
erwanMarmelab May 21, 2025
92b2967
fix child types
erwanMarmelab May 21, 2025
ed24389
improve the story to display every possibility of ListGuesser
erwanMarmelab May 21, 2025
96899d3
adapt test to the new ListGuesser
erwanMarmelab May 21, 2025
ea93745
make the representation prettier with indentation + use children inst…
erwanMarmelab May 26, 2025
46dd322
fix representation
erwanMarmelab May 26, 2025
51dc3cf
adapt tests to this new representation
erwanMarmelab May 26, 2025
6479209
Do not import `DataTable.Col` or `DataTable.NumberCol`
erwanMarmelab May 26, 2025
f1d9dab
use DataTable in Tutorial instead of Datagrid
erwanMarmelab May 26, 2025
902d861
Merge branch 'master' into integrate-datatable-in-guessers
erwanMarmelab May 26, 2025
2431f0f
make representation prettier
erwanMarmelab May 26, 2025
d09f190
Add story + adapt test
erwanMarmelab May 26, 2025
500fa3a
use DataTable in ShowGuesser instead of Datagrid
erwanMarmelab May 26, 2025
b3bdd85
fix missing `key` prop
erwanMarmelab May 26, 2025
66ce6d0
adapt test
erwanMarmelab May 26, 2025
57c5ca6
Merge branch 'integrate-datatable-in-guessers' of github.com:marmelab…
erwanMarmelab May 26, 2025
a912bca
Use children instead of field
erwanMarmelab May 27, 2025
c0745dd
Apply suggestions from code review
erwanMarmelab Jun 3, 2025
85ed6e9
improve and fix the tutorial
erwanMarmelab Jun 3, 2025
e4695e9
Match `Xxx.Xxx` instead of starting with `DataTable.`
erwanMarmelab Jun 3, 2025
5cf92ae
Merge pull request #10754 from marmelab/integrate-datatable-in-guessers
slax57 Jun 3, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
244 changes: 137 additions & 107 deletions docs/Tutorial.md

Large diffs are not rendered by default.

54 changes: 23 additions & 31 deletions packages/ra-ui-materialui/src/detail/ShowGuesser.spec.tsx
Original file line number Diff line number Diff line change
@@ -1,52 +1,44 @@
import * as React from 'react';
import expect from 'expect';
import { render, screen, waitFor } from '@testing-library/react';
import { CoreAdminContext } from 'ra-core';
import { render, screen } from '@testing-library/react';

import { ShowGuesser } from './ShowGuesser';
import { ThemeProvider } from '../theme/ThemeProvider';
import { ShowGuesser } from './ShowGuesser.stories';

describe('<ShowGuesser />', () => {
it('should log the guessed Show view based on the fetched record', async () => {
const logSpy = jest.spyOn(console, 'log').mockImplementation(() => {});
const dataProvider = {
getOne: () =>
Promise.resolve({
data: {
id: 123,
author: 'john doe',
post_id: 6,
score: 3,
body: "Queen, tossing her head through the wood. 'If it had lost something; and she felt sure it.",
created_at: new Date('2012-08-02'),
tags_ids: [1, 2],
},
}),
};
render(
<ThemeProvider>
<CoreAdminContext dataProvider={dataProvider as any}>
<ShowGuesser resource="comments" id={123} enableLog />
</CoreAdminContext>
</ThemeProvider>
);
await waitFor(() => {
screen.getByText('john doe');
});
render(<ShowGuesser />);
await screen.findByText('john doe');
expect(logSpy).toHaveBeenCalledWith(`Guessed Show:

import { DateField, NumberField, ReferenceArrayField, ReferenceField, Show, SimpleShowLayout, TextField } from 'react-admin';
import { ArrayField, BooleanField, DataTable, DateField, EmailField, NumberField, ReferenceArrayField, ReferenceField, RichTextField, Show, SimpleShowLayout, TextField, UrlField } from 'react-admin';

export const CommentShow = () => (
export const BookShow = () => (
<Show>
<SimpleShowLayout>
<TextField source="id" />
<TextField source="author" />
<ArrayField source="authors">
<DataTable>
<DataTable.Col source="id">
<TextField source="id" />
</DataTable.Col>
<DataTable.Col source="name">
<TextField source="name" />
</DataTable.Col>
<DataTable.Col source="dob">
<DateField source="dob" />
</DataTable.Col>
</DataTable>
</ArrayField>
<ReferenceField source="post_id" reference="posts" />
<NumberField source="score" />
<TextField source="body" />
<RichTextField source="description" />
<DateField source="created_at" />
<ReferenceArrayField source="tags_ids" reference="tags" />
<UrlField source="url" />
<EmailField source="email" />
<BooleanField source="isAlreadyPublished" />
</SimpleShowLayout>
</Show>
);`);
Expand Down
48 changes: 48 additions & 0 deletions packages/ra-ui-materialui/src/detail/ShowGuesser.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import * as React from 'react';
import { Admin } from 'react-admin';
import { Resource, TestMemoryRouter } from 'ra-core';
import fakeRestProvider from 'ra-data-fakerest';

import { ShowGuesser as RAShowGuesser } from './ShowGuesser';

export default { title: 'ra-ui-materialui/detail/ShowGuesser' };

const data = {
books: [
{
id: 123,
authors: [
{ id: 1, name: 'john doe', dob: '1990-01-01' },
{ id: 2, name: 'jane doe', dob: '1992-01-01' },
],
post_id: 6,
score: 3,
body: "Queen, tossing her head through the wood. 'If it had lost something; and she felt sure it.",
description: `<p><strong>War and Peace</strong> is a novel by the Russian author <a href="https://en.wikipedia.org/wiki/Leo_Tolstoy">Leo Tolstoy</a>,
published serially, then in its entirety in 1869.</p>
<p>It is regarded as one of Tolstoy's finest literary achievements and remains a classic of world literature.</p>`,
created_at: new Date('2012-08-02'),
tags_ids: [1, 2],
url: 'https://www.myshop.com/tags/top-seller',
email: 'doe@production.com',
isAlreadyPublished: true,
},
],
tags: [
{ id: 1, name: 'top seller' },
{ id: 2, name: 'new' },
],
posts: [
{ id: 6, title: 'War and Peace', body: 'A great novel by Leo Tolstoy' },
],
};

const ShowGuesserWithProdLogs = () => <RAShowGuesser enableLog />;

export const ShowGuesser = () => (
<TestMemoryRouter initialEntries={['/books/123/show']}>
<Admin dataProvider={fakeRestProvider(data)}>
<Resource name="books" show={ShowGuesserWithProdLogs} />
</Admin>
</TestMemoryRouter>
);
1 change: 1 addition & 0 deletions packages/ra-ui-materialui/src/detail/ShowGuesser.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ const ShowViewGuesser = (
)
)
)
.filter(component => !component.match(/[A-Za-z]+\.[A-Za-z]+/i))
.sort();

console.log(
Expand Down
34 changes: 23 additions & 11 deletions packages/ra-ui-materialui/src/detail/showFieldTypes.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as React from 'react';
import { ReactNode } from 'react';
import { Datagrid } from '../list/datagrid/Datagrid';
import type { InferredElement, InferredTypeMap, InputProps } from 'ra-core';
import {
ArrayField,
BooleanField,
Expand All @@ -17,8 +17,7 @@ import {
ChipField,
} from '../field';
import { SimpleShowLayout, SimpleShowLayoutProps } from './SimpleShowLayout';
import { InferredElement, InferredTypeMap, InputProps } from 'ra-core';
import { SingleFieldList } from '../list';
import { DataTable, SingleFieldList } from '../list';

export const showFieldTypes: InferredTypeMap = {
show: {
Expand All @@ -30,18 +29,31 @@ ${children.map(child => ` ${child.getRepresentation()}`).join('\n')}
</SimpleShowLayout>`,
},
array: {
component: ({
children,
...props
}: { children: ReactNode } & InputProps) => (
component: ({ children, ...props }: { children } & InputProps) => (
<ArrayField {...props}>
<Datagrid>{children}</Datagrid>
<DataTable>
{children && children.length > 0
? children.map((child, index) => (
<DataTable.Col key={index} {...child.props}>
{child}
</DataTable.Col>
))
: children}
</DataTable>
</ArrayField>
),
representation: (props: InputProps, children: InferredElement[]) =>
`<ArrayField source="${props.source}"><Datagrid>${children
.map(child => child.getRepresentation())
.join('\n')}</Datagrid></ArrayField>`,
`<ArrayField source="${props.source}">
<DataTable>
${children
.map(
child => `<DataTable.Col source="${child.getProps().source}">
${child.getRepresentation()}
</DataTable.Col>`
)
.join('\n ')}
</DataTable>
</ArrayField>`,
},
boolean: {
component: BooleanField,
Expand Down
114 changes: 69 additions & 45 deletions packages/ra-ui-materialui/src/list/ListGuesser.spec.tsx
Original file line number Diff line number Diff line change
@@ -1,57 +1,81 @@
import * as React from 'react';
import expect from 'expect';
import { render, screen, waitFor } from '@testing-library/react';
import { CoreAdminContext, testDataProvider } from 'ra-core';

import { ListGuesser } from './ListGuesser';
import { ThemeProvider } from '../theme/ThemeProvider';
import { fireEvent, render, screen } from '@testing-library/react';
import { ManyResources } from './ListGuesser.stories';

describe('<ListGuesser />', () => {
it('should log the guessed List view based on the fetched records', async () => {
it('should log the guessed List views based on the fetched records', async () => {
const logSpy = jest.spyOn(console, 'log').mockImplementation(() => {});
const dataProvider = testDataProvider({
getList: () =>
Promise.resolve({
data: [
{
id: 123,
author: 'john doe',
post_id: 6,
score: 3,
body: "Queen, tossing her head through the wood. 'If it had lost something; and she felt sure it.",
created_at: new Date('2012-08-02'),
tags_ids: [1, 2],
},
],
total: 1,
}),
getMany: () => Promise.resolve({ data: [], total: 0 }),
});
render(
<ThemeProvider>
<CoreAdminContext dataProvider={dataProvider as any}>
<ListGuesser resource="comments" enableLog />
</CoreAdminContext>
</ThemeProvider>
);
await waitFor(() => {
screen.getByText('john doe');
});
render(<ManyResources />);
await screen.findAllByText('top seller', undefined, { timeout: 2000 });
expect(logSpy).toHaveBeenCalledWith(`Guessed List:

import { DataTable, DateField, EmailField, List, ReferenceArrayField, ReferenceField } from 'react-admin';

export const ProductList = () => (
<List>
<DataTable>
<DataTable.Col source="id" />
<DataTable.Col source="name" />
<DataTable.NumberCol source="price" />
<DataTable.Col source="category_id">
<ReferenceField source="category_id" reference="categories" />
</DataTable.Col>
<DataTable.Col source="tags_ids">
<ReferenceArrayField source="tags_ids" reference="tags" />
</DataTable.Col>
<DataTable.Col source="last_update">
<DateField source="last_update" />
</DataTable.Col>
<DataTable.Col source="email">
<EmailField source="email" />
</DataTable.Col>
</DataTable>
</List>
);`);
logSpy.mockClear();

fireEvent.click(screen.getByText('Categories'));
await screen.findByText('Jeans');
expect(logSpy).toHaveBeenCalledWith(`Guessed List:

import { ArrayField, BooleanField, ChipField, DataTable, List, SingleFieldList } from 'react-admin';

export const CategoryList = () => (
<List>
<DataTable>
<DataTable.Col source="id" />
<DataTable.Col source="name" />
<DataTable.Col source="alternativeName">
<ArrayField source="alternativeName">
<SingleFieldList>
<ChipField source="name" />
</SingleFieldList>
</ArrayField>
</DataTable.Col>
<DataTable.Col source="isVeganProduction">
<BooleanField source="isVeganProduction" />
</DataTable.Col>
</DataTable>
</List>
);`);

logSpy.mockClear();
fireEvent.click(screen.getByText('Tags'));
await screen.findByText('top seller');
expect(logSpy).toHaveBeenCalledWith(`Guessed List:

import { Datagrid, DateField, List, NumberField, ReferenceArrayField, ReferenceField, TextField } from 'react-admin';
import { DataTable, List, UrlField } from 'react-admin';

export const CommentList = () => (
export const TagList = () => (
<List>
<Datagrid>
<TextField source="id" />
<TextField source="author" />
<ReferenceField source="post_id" reference="posts" />
<NumberField source="score" />
<TextField source="body" />
<DateField source="created_at" />
<ReferenceArrayField source="tags_ids" reference="tags" />
</Datagrid>
<DataTable>
<DataTable.Col source="id" />
<DataTable.Col source="name" />
<DataTable.Col source="url">
<UrlField source="url" />
</DataTable.Col>
</DataTable>
</List>
);`);
});
Expand Down
Loading