Skip to content

Commit ed084b6

Browse files
committed
Fix <Datagrid> rowClick function cannot expand or select
1 parent 30bee8b commit ed084b6

File tree

2 files changed

+172
-112
lines changed

2 files changed

+172
-112
lines changed

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();

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

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -124,23 +124,28 @@ const DatagridRow: React.ForwardRefExoticComponent<
124124
const handleClick = useCallback(
125125
async event => {
126126
event.persist();
127-
const path = await getPathForRecord({
128-
record,
129-
resource,
130-
link:
131-
typeof rowClick === 'function'
132-
? (record, resource) =>
133-
rowClick(record.id, resource, record)
134-
: rowClick,
135-
});
136-
if (rowClick === 'expand') {
127+
let temporaryLink =
128+
typeof rowClick === 'function'
129+
? rowClick(record.id, resource, record)
130+
: rowClick;
131+
132+
const link = isPromise(temporaryLink)
133+
? await temporaryLink
134+
: temporaryLink;
135+
136+
if (link === 'expand') {
137137
handleToggleExpand(event);
138138
return;
139139
}
140-
if (rowClick === 'toggleSelection') {
140+
if (link === 'toggleSelection') {
141141
handleToggleSelection(event);
142142
return;
143143
}
144+
const path = await getPathForRecord({
145+
record,
146+
resource,
147+
link,
148+
});
144149
if (path === false || path == null) {
145150
return;
146151
}
@@ -281,4 +286,7 @@ export const PureDatagridRow = memo(DatagridRow, areEqual);
281286

282287
PureDatagridRow.displayName = 'PureDatagridRow';
283288

289+
const isPromise = (value: any): value is Promise<any> =>
290+
value && typeof value.then === 'function';
291+
284292
export default DatagridRow;

0 commit comments

Comments
 (0)