Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
use react-redux hooks
Browse files Browse the repository at this point in the history
ThieryMichel committed May 21, 2019

Verified

This commit was signed with the committer’s verified signature.
billy1624 Billy Chan
1 parent 292f6dc commit 53516f9
Showing 2 changed files with 214 additions and 123 deletions.
Original file line number Diff line number Diff line change
@@ -1,166 +1,263 @@
import React from 'react';
import assert from 'assert';
import { render } from 'react-testing-library';
import { UnconnectedReferenceManyFieldController as ReferenceManyFieldController } from './ReferenceManyFieldController';

import ReferenceManyFieldController from './ReferenceManyFieldController';
import renderWithRedux from '../../util/renderWithRedux';

describe('<ReferenceManyFieldController />', () => {
it('should set loadedOnce to false when related records are not yet fetched', () => {
const children = jest.fn().mockReturnValue('children');;
const crudGetManyReference = jest.fn();
render(
const children = jest.fn().mockReturnValue('children');
const { dispatch } = renderWithRedux(
<ReferenceManyFieldController
resource="foo"
reference="bar"
target="foo_id"
basePath=""
crudGetManyReference={crudGetManyReference}
>
{children}
</ReferenceManyFieldController>
</ReferenceManyFieldController>,
{
admin: {
resources: {
bar: {
data: {
1: { id: 1, title: 'hello' },
2: { id: 2, title: 'world' },
},
},
},
references: {
oneToMany: {
'foo_bar@fooId_barId': {
ids: [1, 2],
},
},
},
},
}
);
assert.equal(children.mock.calls[0][0].loadedOnce, false);
assert.deepEqual(dispatch.mock.calls[0], [
{
meta: {
fetch: 'GET_MANY_REFERENCE',
onFailure: {
notification: {
body: 'ra.notification.http_error',
level: 'warning',
},
},
relatedTo: 'foo_bar@foo_id_undefined',
resource: 'bar',
},
payload: {
filter: {},
id: undefined,
pagination: { page: 1, perPage: 25 },
sort: { field: 'id', order: 'DESC' },
source: undefined,
target: 'foo_id',
},
type: 'RA/CRUD_GET_MANY_REFERENCE',
},
]);
});

it('should pass data and ids to children function', () => {
const children = jest.fn().mockReturnValue('children');;
const crudGetManyReference = jest.fn();
const children = jest.fn().mockReturnValue('children');
const data = {
1: { id: 1, title: 'hello' },
2: { id: 2, title: 'world' },
};
render(
renderWithRedux(
<ReferenceManyFieldController
resource="foo"
reference="bar"
target="foo_id"
target="fooId"
basePath=""
data={data}
ids={[1, 2]}
crudGetManyReference={crudGetManyReference}
record={{
source: 'barId',
}}
source="source"
>
{children}
</ReferenceManyFieldController>
</ReferenceManyFieldController>,
{
admin: {
resources: {
bar: {
data: {
1: { id: 1, title: 'hello' },
2: { id: 2, title: 'world' },
},
},
},
references: {
oneToMany: {
'foo_bar@fooId_barId': {
ids: [1, 2],
},
},
},
},
}
);
assert.deepEqual(children.mock.calls[0][0].data, data);
assert.deepEqual(children.mock.calls[0][0].ids, [1, 2]);
});

it('should support record with string identifier', () => {
const children = jest.fn().mockReturnValue('children');;
const crudGetManyReference = jest.fn();
const data = {
'abc-1': { id: 'abc-1', title: 'hello' },
'abc-2': { id: 'abc-2', title: 'world' },
};
render(
const children = jest.fn().mockReturnValue('children');
renderWithRedux(
<ReferenceManyFieldController
resource="foo"
reference="bar"
target="foo_id"
target="fooId"
basePath=""
data={data}
ids={['abc-1', 'abc-2']}
crudGetManyReference={crudGetManyReference}
record={{
source: 'barId',
}}
source="source"
>
{children}
</ReferenceManyFieldController>
</ReferenceManyFieldController>,
{
admin: {
resources: {
bar: {
data: {
'abc-1': { id: 'abc-1', title: 'hello' },
'abc-2': { id: 'abc-2', title: 'world' },
},
},
},
references: {
oneToMany: {
'foo_bar@fooId_barId': {
ids: ['abc-1', 'abc-2'],
},
},
},
},
}
);
assert.deepEqual(children.mock.calls[0][0].data, data);
assert.deepEqual(children.mock.calls[0][0].data, {
'abc-1': { id: 'abc-1', title: 'hello' },
'abc-2': { id: 'abc-2', title: 'world' },
});
assert.deepEqual(children.mock.calls[0][0].ids, ['abc-1', 'abc-2']);
});

it('should support record with number identifier', () => {
const children = jest.fn().mockReturnValue('children');;
const crudGetManyReference = jest.fn();
const data = {
1: { id: 1, title: 'hello' },
2: { id: 2, title: 'world' },
};
render(
<ReferenceManyFieldController
resource="foo"
reference="bar"
target="foo_id"
basePath=""
data={data}
ids={[1, 2]}
crudGetManyReference={crudGetManyReference}
>
{children}
</ReferenceManyFieldController>
);
assert.deepEqual(children.mock.calls[0][0].data, data);
assert.deepEqual(children.mock.calls[0][0].ids, [1, 2]);
});

it('should support custom source', () => {
const children = jest.fn().mockReturnValue('children');
const crudGetManyReference = jest.fn();

render(
const { dispatch } = renderWithRedux(
<ReferenceManyFieldController
resource="posts"
reference="comments"
target="post_id"
basePath=""
record={{ id: 'not me', customId: 1 }}
source="customId"
crudGetManyReference={crudGetManyReference}
>
{children}
</ReferenceManyFieldController>
);

assert.equal(crudGetManyReference.mock.calls[0][2], 1);
assert.deepEqual(dispatch.mock.calls[0], [
{
meta: {
fetch: 'GET_MANY_REFERENCE',
onFailure: {
notification: {
body: 'ra.notification.http_error',
level: 'warning',
},
},
relatedTo: 'posts_comments@post_id_1',
resource: 'comments',
},
payload: {
filter: {},
id: 1,
pagination: { page: 1, perPage: 25 },
sort: { field: 'id', order: 'DESC' },
source: 'customId',
target: 'post_id',
},
type: 'RA/CRUD_GET_MANY_REFERENCE',
},
]);
});

it('should call crudGetManyReference when its props changes', () => {
const crudGetManyReference = jest.fn();
const ControllerWrapper = props => (
<ReferenceManyFieldController
record={{ id: 1 }}
resource="foo"
reference="bar"
target="foo_id"
basePath=""
data={{
1: { id: 1, title: 'hello' },
2: { id: 2, title: 'world' },
}}
ids={[1, 2]}
source="id"
crudGetManyReference={crudGetManyReference}
{...props}
>
{() => 'null'}
</ReferenceManyFieldController>
);

const { rerender } = render(<ControllerWrapper />);
const { rerender, dispatch } = renderWithRedux(<ControllerWrapper />);
rerender(<ControllerWrapper sort={{ field: 'id', order: 'ASC' }} />);

expect(crudGetManyReference).toBeCalledTimes(2);
expect(dispatch).toBeCalledTimes(2);

assert.deepEqual(crudGetManyReference.mock.calls[0], [
'bar',
'foo_id',
1,
'foo_bar@foo_id_1',
{ page: 1, perPage: 25 },
{ field: 'id', order: 'DESC' },
{},
'id',
assert.deepEqual(dispatch.mock.calls[0], [
{
meta: {
fetch: 'GET_MANY_REFERENCE',
onFailure: {
notification: {
body: 'ra.notification.http_error',
level: 'warning',
},
},
relatedTo: 'foo_bar@foo_id_1',
resource: 'bar',
},
payload: {
filter: {},
id: 1,
pagination: { page: 1, perPage: 25 },
sort: { field: 'id', order: 'DESC' },
source: 'id',
target: 'foo_id',
},
type: 'RA/CRUD_GET_MANY_REFERENCE',
},
]);

assert.deepEqual(crudGetManyReference.mock.calls[1], [
'bar',
'foo_id',
1,
'foo_bar@foo_id_1',
{ page: 1, perPage: 25 },
{ field: 'id', order: 'ASC' },
{},
'id',
assert.deepEqual(dispatch.mock.calls[1], [
{
meta: {
fetch: 'GET_MANY_REFERENCE',
onFailure: {
notification: {
body: 'ra.notification.http_error',
level: 'warning',
},
},
relatedTo: 'foo_bar@foo_id_1',
resource: 'bar',
},
payload: {
filter: {},
id: 1,
pagination: { page: 1, perPage: 25 },
sort: { field: 'id', order: 'ASC' },
source: 'id',
target: 'foo_id',
},
type: 'RA/CRUD_GET_MANY_REFERENCE',
},
]);
});
});
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { ReactNode, useState, useReducer, useEffect } from 'react';
import { connect } from 'react-redux';
import { ReactNode, useState, useReducer, useEffect, useMemo } from 'react';
// @ts-ignore
import { useSelector, useDispatch } from 'react-redux';
import get from 'lodash/get';

import { crudGetManyReference as crudGetManyReferenceAction } from '../../actions';
import { crudGetManyReference } from '../../actions';
import {
SORT_ASC,
SORT_DESC,
@@ -32,7 +33,6 @@ interface ChildrenFuncParams {
interface Props {
basePath: string;
children: (params: ChildrenFuncParams) => ReactNode;
crudGetManyReference: Dispatch<typeof crudGetManyReferenceAction>;
data?: RecordMap;
filter?: any;
ids?: any[];
@@ -106,29 +106,37 @@ const defaultFilter = {};
* ...
* </ReferenceManyField>
*/
export const UnconnectedReferenceManyFieldController = ({
export const ReferenceManyFieldController = ({
resource,
reference,
record,
target,
filter = defaultFilter,
source,
crudGetManyReference,
data,
ids,
children,
basePath,
total,
perPage = 25,
sort = { field: 'id', order: 'DESC' },
}) => {
const referenceId = get(record, source);
const relatedTo = useMemo(
() => nameRelatedTo(reference, referenceId, resource, target, filter),
[filter, reference, referenceId, resource, target]
);
const ids = useSelector(selectIds(relatedTo), [relatedTo]);
const data = useSelector(selectData(reference, relatedTo), [
reference,
relatedTo,
]);
const total = useSelector(selectTotal(relatedTo), [relatedTo]);
const [page, setPage] = useState(1);
const [currentPerPage, setPerPage] = useState(perPage);
useEffect(() => setPerPage(perPage), [perPage]);
const [currentSort, setSort] = useReducer(sortReducer, sort);
useEffect(() => setSort(sort), [sort.field, sort.order]);

const dispatch = useDispatch();

useEffect(
fetchReferences({
reference,
@@ -137,10 +145,10 @@ export const UnconnectedReferenceManyFieldController = ({
target,
filter,
source,
crudGetManyReference,
page,
perPage: currentPerPage,
sort: currentSort,
dispatch,
}),
[
reference,
@@ -188,7 +196,7 @@ const fetchReferences = ({
target,
filter,
source,
crudGetManyReference,
dispatch,
page,
perPage,
sort,
@@ -201,38 +209,24 @@ const fetchReferences = ({
filter
);

crudGetManyReference(
reference,
target,
referenceId,
relatedTo,
{ page, perPage },
sort,
filter,
source
dispatch(
crudGetManyReference(
reference,
target,
referenceId,
relatedTo,
{ page, perPage },
sort,
filter,
source
)
);
};

function mapStateToProps(state, props) {
const relatedTo = nameRelatedTo(
props.reference,
get(props.record, props.source),
props.resource,
props.target,
props.filter
);
return {
data: getReferences(state, props.reference, relatedTo),
ids: getIds(state, relatedTo),
total: getTotal(state, relatedTo),
};
}
const selectData = (reference, relatedTo) => state =>
getReferences(state, reference, relatedTo);

const ReferenceManyFieldController = connect(
mapStateToProps,
{
crudGetManyReference: crudGetManyReferenceAction,
}
)(UnconnectedReferenceManyFieldController);
const selectIds = relatedTo => state => getIds(state, relatedTo);
const selectTotal = relatedTo => state => getTotal(state, relatedTo);

export default ReferenceManyFieldController;

0 comments on commit 53516f9

Please sign in to comment.