Skip to content

Commit

Permalink
Fix datagrid usage inside reference array input
Browse files Browse the repository at this point in the history
  • Loading branch information
Travis CI committed Sep 30, 2021
1 parent 695e0a2 commit fd895a8
Show file tree
Hide file tree
Showing 7 changed files with 135 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ export const useReferenceArrayInputController = (
// only the missing references when the input value changes
const inputValue = useRef(input.value);
const [idsToFetch, setIdsToFetch] = useState(input.value);
const [idsToGetFromStore, setIdsToGetFromStore] = useState([]);
const [idsToGetFromStore, setIdsToGetFromStore] = useState(EmptyArray);
const referenceRecordsFromStore = useSelector((state: ReduxState) =>
idsToGetFromStore.map(id => state.admin.resources[reference].data[id])
);
Expand Down Expand Up @@ -121,18 +121,23 @@ export const useReferenceArrayInputController = (
const form = useForm();
const onSelect = useCallback(
(newIds: Identifier[]) => {
// This could happen when user unselect all items using the datagrid for instance
if (newIds.length === 0) {
form.change(input.name, EmptyArray);
return;
}

const newValue = new Set(input.value);
newIds.forEach(newId => {
newValue.add(newId);
});

form.change(input.name, Array.from(newValue));
},
[form, input.name, input.value]
[form, input.value, input.name]
);

const onUnselectItems = useCallback(() => {
form.change(input.name, []);
form.change(input.name, EmptyArray);
}, [form, input.name]);

const onToggleItem = useCallback(
Expand Down Expand Up @@ -244,7 +249,7 @@ export const useReferenceArrayInputController = (
data: referenceRecordsFetched,
loaded,
refetch: refetchGetMany,
} = useGetMany(reference, idsToFetch || []);
} = useGetMany(reference, idsToFetch || EmptyArray);

const referenceRecords = referenceRecordsFetched
? referenceRecordsFetched.concat(referenceRecordsFromStore)
Expand Down Expand Up @@ -306,7 +311,7 @@ export const useReferenceArrayInputController = (
hideFilter,
// For the ListContext, we don't want to always display the selected items first.
// Indeed it wouldn't work well regarding sorting and pagination
ids: matchingReferencesIds || [],
ids: matchingReferencesIds || EmptyArray,
loaded,
loading: dataStatus.waiting,
onSelect,
Expand All @@ -316,7 +321,7 @@ export const useReferenceArrayInputController = (
perPage,
refetch,
resource,
selectedIds: input.value,
selectedIds: input.value || EmptyArray,
setFilter,
setFilters,
setPage,
Expand All @@ -330,6 +335,8 @@ export const useReferenceArrayInputController = (
};
};

const EmptyArray = [];

// concatenate and deduplicate two lists of records
const mergeReferences = (ref1: Record[], ref2: Record[]): Record[] => {
const res = [...ref1];
Expand Down
2 changes: 2 additions & 0 deletions packages/ra-language-english/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ const englishMessages: TranslationMessages = {
remove: 'Remove',
save: 'Save',
search: 'Search',
select_all: 'Select all',
select_row: 'Select this row',
show: 'Show',
sort: 'Sort',
undo: 'Undo',
Expand Down
2 changes: 2 additions & 0 deletions packages/ra-language-french/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ const frenchMessages: TranslationMessages = {
remove_filter: 'Supprimer ce filtre',
remove: 'Supprimer',
save: 'Enregistrer',
select_all: 'Tout sélectionner',
select_row: 'Sélectionner cette ligne',
search: 'Rechercher',
show: 'Afficher',
sort: 'Trier',
Expand Down
108 changes: 104 additions & 4 deletions packages/ra-ui-materialui/src/input/ReferenceArrayInput.spec.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import * as React from 'react';
import { render } from '@testing-library/react';
import ReferenceArrayInput, {
ReferenceArrayInputView,
} from './ReferenceArrayInput';
import { render, waitFor, within, fireEvent } from '@testing-library/react';
import { Form } from 'react-final-form';
import { useListContext } from 'ra-core';
import { renderWithRedux } from 'ra-test';
import addDays from 'date-fns/add_days';
import { Datagrid } from '../list';
import { TextField } from '../field';
import ReferenceArrayInput, {
ReferenceArrayInputView,
} from './ReferenceArrayInput';

describe('<ReferenceArrayInput />', () => {
const defaultProps = {
Expand Down Expand Up @@ -227,4 +230,101 @@ describe('<ReferenceArrayInput />', () => {
);
expect(getByLabelText('total').innerHTML).toEqual('2');
});

test('should allow to use a Datagrid', async () => {
const { getByLabelText, queryByText } = renderWithRedux(
<Form
onSubmit={jest.fn()}
initialValues={{ tag_ids: [5] }}
render={() => (
<ReferenceArrayInput
reference="tags"
resource="posts"
source="tag_ids"
basePath="/posts"
>
<Datagrid
hasBulkActions={true}
rowClick="toggleSelection"
>
<TextField source="name" />
</Datagrid>
</ReferenceArrayInput>
)}
/>,
{
admin: {
references: {
possibleValues: {
'posts@tag_ids': [5, 6],
},
},
resources: {
tags: {
list: {
cachedRequests: {
[JSON.stringify({
pagination: { page: 1, perPage: 25 },
sort: {
field: 'id',
order: 'DESC',
},
filter: {},
})]: {
ids: [5, 6],
total: 2,
validity: addDays(new Date(), 1),
},
},
},
data: {
5: { id: 5, name: 'test1' },
6: { id: 6, name: 'test2' },
},
},
},
},
}
);

await waitFor(() => {
expect(queryByText('test1')).not.toBeNull();
expect(queryByText('test2')).not.toBeNull();
});

const checkBoxTest1 = within(queryByText('test1').closest('tr'))
.getByLabelText('ra.action.select_row')
.querySelector('input');

const checkBoxTest2 = within(queryByText('test2').closest('tr'))
.getByLabelText('ra.action.select_row')
.querySelector('input');

const checkBoxAll = getByLabelText(
'ra.action.select_all'
).querySelector('input');

expect(checkBoxTest1.checked).toEqual(true);
expect(checkBoxTest2.checked).toEqual(false);
fireEvent.click(checkBoxTest2);

await waitFor(() => {
expect(checkBoxTest2.checked).toEqual(true);
expect(checkBoxAll.checked).toEqual(true);
});

fireEvent.click(checkBoxAll);
await waitFor(() => {
expect(checkBoxTest1.checked).toEqual(false);
expect(checkBoxTest2.checked).toEqual(false);
expect(checkBoxAll.checked).toEqual(false);
});

fireEvent.click(checkBoxAll);
await waitFor(() => {
expect(checkBoxTest1.checked).toEqual(true);
expect(checkBoxTest2.checked).toEqual(true);
expect(checkBoxAll.checked).toEqual(true);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,6 @@ const ReferenceArrayInput = ({
);

const translate = useTranslate();

return (
<ResourceContextProvider value={props.reference}>
<ReferenceArrayInputContextProvider value={controllerProps}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
Record,
RecordMap,
SortPayload,
useTranslate,
} from 'ra-core';
import { Checkbox, TableCell, TableHead, TableRow } from '@material-ui/core';
import classnames from 'classnames';
Expand All @@ -31,6 +32,7 @@ export const DatagridHeader = (props: DatagridHeaderProps) => {
isRowSelectable,
} = props;
const resource = useResourceContext(props);
const translate = useTranslate();
const {
currentSort,
data,
Expand Down Expand Up @@ -98,6 +100,9 @@ export const DatagridHeader = (props: DatagridHeaderProps) => {
className={classes.headerCell}
>
<Checkbox
aria-label={translate('ra.action.select_all', {
_: 'Select all',
})}
className="select-all"
color="primary"
checked={
Expand Down
11 changes: 8 additions & 3 deletions packages/ra-ui-materialui/src/list/datagrid/DatagridRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,13 @@ import {
Checkbox,
} from '@material-ui/core';
import {
linkToRecord,
useExpanded,
Identifier,
linkToRecord,
Record,
useResourceContext,
RecordContextProvider,
useExpanded,
useResourceContext,
useTranslate,
} from 'ra-core';
import { shallowEqual } from 'react-redux';
import { useHistory } from 'react-router-dom';
Expand Down Expand Up @@ -62,6 +63,7 @@ const DatagridRow: FC<DatagridRowProps> = React.forwardRef((props, ref) => {
} = props;

const context = useDatagridContext();
const translate = useTranslate();
const expandable =
(!context ||
!context.isRowExpandable ||
Expand Down Expand Up @@ -174,6 +176,9 @@ const DatagridRow: FC<DatagridRowProps> = React.forwardRef((props, ref) => {
<TableCell padding="checkbox">
{selectable && (
<Checkbox
aria-label={translate('ra.action.select_row', {
_: 'Select this row',
})}
color="primary"
className={`select-item ${classes.checkbox}`}
checked={selected}
Expand Down

0 comments on commit fd895a8

Please sign in to comment.