Skip to content

Commit 3e611ef

Browse files
authored
Merge pull request marmelab#10404 from marmelab/fix-datagrid-row-click
Fix `<Datagrid>` `rowClick` function cannot expand or select
2 parents d4d456a + f9768b4 commit 3e611ef

File tree

3 files changed

+229
-114
lines changed

3 files changed

+229
-114
lines changed

packages/ra-ui-materialui/src/list/datagrid/Datagrid.stories.tsx

Lines changed: 57 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -471,7 +471,11 @@ export const RowClickFalse = () => (
471471

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

474-
export const FullApp = () => (
474+
export const FullApp = ({
475+
rowClick,
476+
}: {
477+
rowClick?: DatagridRowProps['rowClick'];
478+
}) => (
475479
<AdminContext
476480
dataProvider={dataProvider}
477481
i18nProvider={polyglotI18nProvider(() => defaultMessages, 'en')}
@@ -481,7 +485,10 @@ export const FullApp = () => (
481485
name="books"
482486
list={() => (
483487
<List>
484-
<Datagrid>
488+
<Datagrid
489+
expand={<ExpandDetails />}
490+
rowClick={rowClick}
491+
>
485492
<TextField source="id" />
486493
<TextField source="title" />
487494
<TextField source="author" />
@@ -490,11 +497,59 @@ export const FullApp = () => (
490497
</List>
491498
)}
492499
edit={EditGuesser}
500+
show={ShowGuesser}
493501
/>
494502
</AdminUI>
495503
</AdminContext>
496504
);
497505

506+
FullApp.argTypes = {
507+
rowClick: {
508+
options: [
509+
'inferred',
510+
'show',
511+
'edit',
512+
'no-link',
513+
'expand',
514+
'toggleSelection',
515+
'function to expand',
516+
'function to toggleSelection',
517+
],
518+
mapping: {
519+
inferred: undefined,
520+
show: 'show',
521+
edit: 'edit',
522+
'no-link': false,
523+
expand: 'expand',
524+
toggleSelection: 'toggleSelection',
525+
'function to expand': (id, resource, record) => {
526+
if (process.env.NODE_ENV === 'development') {
527+
console.log('function to expand', id, resource, record);
528+
}
529+
return 'expand';
530+
},
531+
'function to toggleSelection': (id, resource, record) => {
532+
if (process.env.NODE_ENV === 'development') {
533+
console.log(
534+
'function to toggleSelection',
535+
id,
536+
resource,
537+
record
538+
);
539+
}
540+
return 'toggleSelection';
541+
},
542+
},
543+
control: { type: 'select' },
544+
},
545+
};
546+
547+
const ExpandDetails = () => {
548+
const record = useRecordContext();
549+
550+
return <div>Expand: {record?.title}</div>;
551+
};
552+
498553
const MyDatagridRow = ({
499554
onToggleItem,
500555
children,

packages/ra-ui-materialui/src/list/datagrid/DatagridRow.spec.tsx

Lines changed: 153 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -98,119 +98,171 @@ describe('<DatagridRow />', () => {
9898
};
9999

100100
describe('rowClick', () => {
101-
it("should redirect to edit page if the 'edit' option is selected", async () => {
102-
let spy = jest.fn();
103-
render(
104-
<LocationSpy spy={spy}>
105-
<RecordContextProvider value={defaultRecord}>
106-
<DatagridRow {...defaultProps} rowClick="edit">
107-
<TitleField />
108-
</DatagridRow>
109-
</RecordContextProvider>
110-
</LocationSpy>
111-
);
112-
const cell = screen.getByText('hello');
113-
const row = cell.closest('tr');
114-
if (!row) {
115-
throw new Error('row not found');
101+
it.each([
102+
{ rowClick: 'edit', description: 'passed directly' },
103+
{
104+
rowClick: () => 'edit',
105+
description: 'from a rowClick function',
106+
},
107+
{
108+
rowClick: async () => 'edit',
109+
description: 'from an async rowClick function',
110+
},
111+
])(
112+
"should redirect to edit page if the 'edit' option is $description",
113+
async ({ rowClick }) => {
114+
let spy = jest.fn();
115+
render(
116+
<LocationSpy spy={spy}>
117+
<RecordContextProvider value={defaultRecord}>
118+
<DatagridRow {...defaultProps} rowClick={rowClick}>
119+
<TitleField />
120+
</DatagridRow>
121+
</RecordContextProvider>
122+
</LocationSpy>
123+
);
124+
const cell = screen.getByText('hello');
125+
const row = cell.closest('tr');
126+
if (!row) {
127+
throw new Error('row not found');
128+
}
129+
expect(
130+
row.classList.contains('RaDatagrid-clickableRow')
131+
).toBeTruthy();
132+
fireEvent.click(row);
133+
134+
await waitFor(() => {
135+
expect(spy).toHaveBeenCalledWith(
136+
expect.objectContaining({ pathname: '/posts/15' })
137+
);
138+
});
116139
}
117-
expect(
118-
row.classList.contains('RaDatagrid-clickableRow')
119-
).toBeTruthy();
120-
fireEvent.click(row);
140+
);
121141

122-
await waitFor(() => {
123-
expect(spy).toHaveBeenCalledWith(
124-
expect.objectContaining({ pathname: '/posts/15' })
142+
it.each([
143+
{ rowClick: 'show', description: 'passed directly' },
144+
{
145+
rowClick: () => 'show',
146+
description: 'from a rowClick function',
147+
},
148+
{
149+
rowClick: async () => 'show',
150+
description: 'from an async rowClick function',
151+
},
152+
])(
153+
"should redirect to show page if the 'show' option is $description",
154+
async ({ rowClick }) => {
155+
let spy = jest.fn();
156+
render(
157+
<LocationSpy spy={spy}>
158+
<RecordContextProvider value={defaultRecord}>
159+
<DatagridRow {...defaultProps} rowClick={rowClick}>
160+
<TitleField />
161+
</DatagridRow>
162+
</RecordContextProvider>
163+
</LocationSpy>
125164
);
126-
});
127-
});
165+
const cell = screen.getByText('hello');
166+
const row = cell.closest('tr');
167+
if (!row) {
168+
throw new Error('row not found');
169+
}
170+
expect(
171+
row.classList.contains('RaDatagrid-clickableRow')
172+
).toBeTruthy();
173+
fireEvent.click(row);
128174

129-
it("should redirect to show page if the 'show' option is selected", async () => {
130-
let spy = jest.fn();
131-
render(
132-
<LocationSpy spy={spy}>
175+
await waitFor(() => {
176+
expect(spy).toHaveBeenCalledWith(
177+
expect.objectContaining({ pathname: '/posts/15/show' })
178+
);
179+
});
180+
}
181+
);
182+
183+
it.each([
184+
{ rowClick: 'expand', description: 'passed directly' },
185+
{
186+
rowClick: () => 'expand',
187+
description: 'from a rowClick function',
188+
},
189+
{
190+
rowClick: async () => 'expand',
191+
description: 'from an async rowClick function',
192+
},
193+
])(
194+
"should change the expand state if the 'expand' option is $description",
195+
async ({ rowClick }) => {
196+
render(
133197
<RecordContextProvider value={defaultRecord}>
134-
<DatagridRow {...defaultProps} rowClick="show">
198+
<DatagridRow
199+
{...defaultProps}
200+
rowClick={rowClick}
201+
expand={<ExpandPanel />}
202+
>
135203
<TitleField />
136204
</DatagridRow>
137205
</RecordContextProvider>
138-
</LocationSpy>
139-
);
140-
const cell = screen.getByText('hello');
141-
const row = cell.closest('tr');
142-
if (!row) {
143-
throw new Error('row not found');
144-
}
145-
expect(
146-
row.classList.contains('RaDatagrid-clickableRow')
147-
).toBeTruthy();
148-
fireEvent.click(row);
149-
150-
await waitFor(() => {
151-
expect(spy).toHaveBeenCalledWith(
152-
expect.objectContaining({ pathname: '/posts/15/show' })
153206
);
154-
});
155-
});
156-
157-
it("should change the expand state if the 'expand' option is selected", async () => {
158-
render(
159-
<RecordContextProvider value={defaultRecord}>
160-
<DatagridRow
161-
{...defaultProps}
162-
rowClick="expand"
163-
expand={<ExpandPanel />}
164-
>
165-
<TitleField />
166-
</DatagridRow>
167-
</RecordContextProvider>
168-
);
169-
expect(screen.queryAllByText('expanded')).toHaveLength(0);
170-
const cell = screen.getByText('hello');
171-
const row = cell.closest('tr');
172-
if (!row) {
173-
throw new Error('row not found');
174-
}
175-
expect(
176-
row.classList.contains('RaDatagrid-clickableRow')
177-
).toBeTruthy();
178-
fireEvent.click(row);
179-
await waitFor(() => {
180-
expect(screen.queryAllByText('expanded')).toHaveLength(1);
181-
});
182-
fireEvent.click(row);
183-
await waitFor(() => {
184207
expect(screen.queryAllByText('expanded')).toHaveLength(0);
185-
});
186-
});
208+
const cell = screen.getByText('hello');
209+
const row = cell.closest('tr');
210+
if (!row) {
211+
throw new Error('row not found');
212+
}
213+
expect(
214+
row.classList.contains('RaDatagrid-clickableRow')
215+
).toBeTruthy();
216+
fireEvent.click(row);
217+
await waitFor(() => {
218+
expect(screen.queryAllByText('expanded')).toHaveLength(1);
219+
});
220+
fireEvent.click(row);
221+
await waitFor(() => {
222+
expect(screen.queryAllByText('expanded')).toHaveLength(0);
223+
});
224+
}
225+
);
187226

188-
it("should execute the onToggleItem function if the 'toggleSelection' option is selected", async () => {
189-
const onToggleItem = jest.fn();
190-
render(
191-
<RecordContextProvider value={defaultRecord}>
192-
<DatagridRow
193-
{...defaultProps}
194-
onToggleItem={onToggleItem}
195-
rowClick="toggleSelection"
196-
>
197-
<TitleField />
198-
</DatagridRow>
199-
</RecordContextProvider>
200-
);
201-
const cell = screen.getByText('hello');
202-
const row = cell.closest('tr');
203-
if (!row) {
204-
throw new Error('row not found');
227+
it.each([
228+
{ rowClick: 'toggleSelection', description: 'passed directly' },
229+
{
230+
rowClick: () => 'toggleSelection',
231+
description: 'from a rowClick function',
232+
},
233+
{
234+
rowClick: async () => 'toggleSelection',
235+
description: 'from an async rowClick function',
236+
},
237+
])(
238+
"should execute the onToggleItem function if the 'toggleSelection' option is $description",
239+
async ({ rowClick }) => {
240+
const onToggleItem = jest.fn();
241+
render(
242+
<RecordContextProvider value={defaultRecord}>
243+
<DatagridRow
244+
{...defaultProps}
245+
onToggleItem={onToggleItem}
246+
rowClick={rowClick}
247+
>
248+
<TitleField />
249+
</DatagridRow>
250+
</RecordContextProvider>
251+
);
252+
const cell = screen.getByText('hello');
253+
const row = cell.closest('tr');
254+
if (!row) {
255+
throw new Error('row not found');
256+
}
257+
expect(
258+
row.classList.contains('RaDatagrid-clickableRow')
259+
).toBeTruthy();
260+
fireEvent.click(row);
261+
await waitFor(() => {
262+
expect(onToggleItem.mock.calls.length).toEqual(1);
263+
});
205264
}
206-
expect(
207-
row.classList.contains('RaDatagrid-clickableRow')
208-
).toBeTruthy();
209-
fireEvent.click(row);
210-
await waitFor(() => {
211-
expect(onToggleItem.mock.calls.length).toEqual(1);
212-
});
213-
});
265+
);
214266

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

0 commit comments

Comments
 (0)