Skip to content

Commit

Permalink
Merge pull request #10404 from marmelab/fix-datagrid-row-click
Browse files Browse the repository at this point in the history
Fix `<Datagrid>` `rowClick` function cannot expand or select
  • Loading branch information
slax57 authored Dec 9, 2024
2 parents d4d456a + f9768b4 commit 3e611ef
Show file tree
Hide file tree
Showing 3 changed files with 229 additions and 114 deletions.
59 changes: 57 additions & 2 deletions packages/ra-ui-materialui/src/list/datagrid/Datagrid.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -471,7 +471,11 @@ export const RowClickFalse = () => (

const dataProvider = fakeRestDataProvider({ books: data });

export const FullApp = () => (
export const FullApp = ({
rowClick,
}: {
rowClick?: DatagridRowProps['rowClick'];
}) => (
<AdminContext
dataProvider={dataProvider}
i18nProvider={polyglotI18nProvider(() => defaultMessages, 'en')}
Expand All @@ -481,7 +485,10 @@ export const FullApp = () => (
name="books"
list={() => (
<List>
<Datagrid>
<Datagrid
expand={<ExpandDetails />}
rowClick={rowClick}
>
<TextField source="id" />
<TextField source="title" />
<TextField source="author" />
Expand All @@ -490,11 +497,59 @@ export const FullApp = () => (
</List>
)}
edit={EditGuesser}
show={ShowGuesser}
/>
</AdminUI>
</AdminContext>
);

FullApp.argTypes = {
rowClick: {
options: [
'inferred',
'show',
'edit',
'no-link',
'expand',
'toggleSelection',
'function to expand',
'function to toggleSelection',
],
mapping: {
inferred: undefined,
show: 'show',
edit: 'edit',
'no-link': false,
expand: 'expand',
toggleSelection: 'toggleSelection',
'function to expand': (id, resource, record) => {
if (process.env.NODE_ENV === 'development') {
console.log('function to expand', id, resource, record);
}
return 'expand';
},
'function to toggleSelection': (id, resource, record) => {
if (process.env.NODE_ENV === 'development') {
console.log(
'function to toggleSelection',
id,
resource,
record
);
}
return 'toggleSelection';
},
},
control: { type: 'select' },
},
};

const ExpandDetails = () => {
const record = useRecordContext();

return <div>Expand: {record?.title}</div>;
};

const MyDatagridRow = ({
onToggleItem,
children,
Expand Down
254 changes: 153 additions & 101 deletions packages/ra-ui-materialui/src/list/datagrid/DatagridRow.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -98,119 +98,171 @@ describe('<DatagridRow />', () => {
};

describe('rowClick', () => {
it("should redirect to edit page if the 'edit' option is selected", async () => {
let spy = jest.fn();
render(
<LocationSpy spy={spy}>
<RecordContextProvider value={defaultRecord}>
<DatagridRow {...defaultProps} rowClick="edit">
<TitleField />
</DatagridRow>
</RecordContextProvider>
</LocationSpy>
);
const cell = screen.getByText('hello');
const row = cell.closest('tr');
if (!row) {
throw new Error('row not found');
it.each([
{ rowClick: 'edit', description: 'passed directly' },
{
rowClick: () => 'edit',
description: 'from a rowClick function',
},
{
rowClick: async () => 'edit',
description: 'from an async rowClick function',
},
])(
"should redirect to edit page if the 'edit' option is $description",
async ({ rowClick }) => {
let spy = jest.fn();
render(
<LocationSpy spy={spy}>
<RecordContextProvider value={defaultRecord}>
<DatagridRow {...defaultProps} rowClick={rowClick}>
<TitleField />
</DatagridRow>
</RecordContextProvider>
</LocationSpy>
);
const cell = screen.getByText('hello');
const row = cell.closest('tr');
if (!row) {
throw new Error('row not found');
}
expect(
row.classList.contains('RaDatagrid-clickableRow')
).toBeTruthy();
fireEvent.click(row);

await waitFor(() => {
expect(spy).toHaveBeenCalledWith(
expect.objectContaining({ pathname: '/posts/15' })
);
});
}
expect(
row.classList.contains('RaDatagrid-clickableRow')
).toBeTruthy();
fireEvent.click(row);
);

await waitFor(() => {
expect(spy).toHaveBeenCalledWith(
expect.objectContaining({ pathname: '/posts/15' })
it.each([
{ rowClick: 'show', description: 'passed directly' },
{
rowClick: () => 'show',
description: 'from a rowClick function',
},
{
rowClick: async () => 'show',
description: 'from an async rowClick function',
},
])(
"should redirect to show page if the 'show' option is $description",
async ({ rowClick }) => {
let spy = jest.fn();
render(
<LocationSpy spy={spy}>
<RecordContextProvider value={defaultRecord}>
<DatagridRow {...defaultProps} rowClick={rowClick}>
<TitleField />
</DatagridRow>
</RecordContextProvider>
</LocationSpy>
);
});
});
const cell = screen.getByText('hello');
const row = cell.closest('tr');
if (!row) {
throw new Error('row not found');
}
expect(
row.classList.contains('RaDatagrid-clickableRow')
).toBeTruthy();
fireEvent.click(row);

it("should redirect to show page if the 'show' option is selected", async () => {
let spy = jest.fn();
render(
<LocationSpy spy={spy}>
await waitFor(() => {
expect(spy).toHaveBeenCalledWith(
expect.objectContaining({ pathname: '/posts/15/show' })
);
});
}
);

it.each([
{ rowClick: 'expand', description: 'passed directly' },
{
rowClick: () => 'expand',
description: 'from a rowClick function',
},
{
rowClick: async () => 'expand',
description: 'from an async rowClick function',
},
])(
"should change the expand state if the 'expand' option is $description",
async ({ rowClick }) => {
render(
<RecordContextProvider value={defaultRecord}>
<DatagridRow {...defaultProps} rowClick="show">
<DatagridRow
{...defaultProps}
rowClick={rowClick}
expand={<ExpandPanel />}
>
<TitleField />
</DatagridRow>
</RecordContextProvider>
</LocationSpy>
);
const cell = screen.getByText('hello');
const row = cell.closest('tr');
if (!row) {
throw new Error('row not found');
}
expect(
row.classList.contains('RaDatagrid-clickableRow')
).toBeTruthy();
fireEvent.click(row);

await waitFor(() => {
expect(spy).toHaveBeenCalledWith(
expect.objectContaining({ pathname: '/posts/15/show' })
);
});
});

it("should change the expand state if the 'expand' option is selected", async () => {
render(
<RecordContextProvider value={defaultRecord}>
<DatagridRow
{...defaultProps}
rowClick="expand"
expand={<ExpandPanel />}
>
<TitleField />
</DatagridRow>
</RecordContextProvider>
);
expect(screen.queryAllByText('expanded')).toHaveLength(0);
const cell = screen.getByText('hello');
const row = cell.closest('tr');
if (!row) {
throw new Error('row not found');
}
expect(
row.classList.contains('RaDatagrid-clickableRow')
).toBeTruthy();
fireEvent.click(row);
await waitFor(() => {
expect(screen.queryAllByText('expanded')).toHaveLength(1);
});
fireEvent.click(row);
await waitFor(() => {
expect(screen.queryAllByText('expanded')).toHaveLength(0);
});
});
const cell = screen.getByText('hello');
const row = cell.closest('tr');
if (!row) {
throw new Error('row not found');
}
expect(
row.classList.contains('RaDatagrid-clickableRow')
).toBeTruthy();
fireEvent.click(row);
await waitFor(() => {
expect(screen.queryAllByText('expanded')).toHaveLength(1);
});
fireEvent.click(row);
await waitFor(() => {
expect(screen.queryAllByText('expanded')).toHaveLength(0);
});
}
);

it("should execute the onToggleItem function if the 'toggleSelection' option is selected", async () => {
const onToggleItem = jest.fn();
render(
<RecordContextProvider value={defaultRecord}>
<DatagridRow
{...defaultProps}
onToggleItem={onToggleItem}
rowClick="toggleSelection"
>
<TitleField />
</DatagridRow>
</RecordContextProvider>
);
const cell = screen.getByText('hello');
const row = cell.closest('tr');
if (!row) {
throw new Error('row not found');
it.each([
{ rowClick: 'toggleSelection', description: 'passed directly' },
{
rowClick: () => 'toggleSelection',
description: 'from a rowClick function',
},
{
rowClick: async () => 'toggleSelection',
description: 'from an async rowClick function',
},
])(
"should execute the onToggleItem function if the 'toggleSelection' option is $description",
async ({ rowClick }) => {
const onToggleItem = jest.fn();
render(
<RecordContextProvider value={defaultRecord}>
<DatagridRow
{...defaultProps}
onToggleItem={onToggleItem}
rowClick={rowClick}
>
<TitleField />
</DatagridRow>
</RecordContextProvider>
);
const cell = screen.getByText('hello');
const row = cell.closest('tr');
if (!row) {
throw new Error('row not found');
}
expect(
row.classList.contains('RaDatagrid-clickableRow')
).toBeTruthy();
fireEvent.click(row);
await waitFor(() => {
expect(onToggleItem.mock.calls.length).toEqual(1);
});
}
expect(
row.classList.contains('RaDatagrid-clickableRow')
).toBeTruthy();
fireEvent.click(row);
await waitFor(() => {
expect(onToggleItem.mock.calls.length).toEqual(1);
});
});
);

it('should not execute the onToggleItem function if the row is not selectable', () => {
const onToggleItem = jest.fn();
Expand Down
Loading

0 comments on commit 3e611ef

Please sign in to comment.