Repository: api-platform/admin
Pull request #80 (2018-02-22 20:39:17)

Extract logic from AdminBuilder to export resourceFactory
| Q             | A
| ------------- | ---
| Bug fix?      | no
| New feature?  | no
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | 
| License       | MIT
| Doc PR        | 

It would be easier create custom Admin app (not using `admin-on-rest`'s Admin component), or inject into existing one.

Todo:
- [x] export function
- [x] add test

dunglas: Looks good to me, can you just add a test for this new function please?
k911: @dunglas Yeah sure, I'll update this as soon as possible

Patch:
diff --git a/src/AdminBuilder.js b/src/AdminBuilder.js
index abffab8..16e5036 100644
--- a/src/AdminBuilder.js
+++ b/src/AdminBuilder.js
@@ -1,48 +1,19 @@
 import Api from '@api-platform/api-doc-parser/lib/Api';
-import {Admin, Delete, Resource} from 'admin-on-rest';
+import {Admin} from 'admin-on-rest';
 import PropTypes from 'prop-types';
 import React from 'react';
-import Create from './Create';
-import Edit from './Edit';
 import fieldFactory from './fieldFactory';
 import inputFactory from './inputFactory';
-import List from './List';
-import Show from './Show';
+import resourceFactory from './resourceFactory';
 
 const AdminBuilder = props => {
   const {api, fieldFactory, inputFactory, title = api.title} = props;
 
   return (
     <Admin {...props} title={title}>
-      {api.resources.map(resource => {
-        const {
-          create = Create,
-          edit = Edit,
-          list = List,
-          name,
-          props,
-          remove = Delete,
-          show = Show,
-        } = resource;
-        return (
-          <Resource
-            {...props}
-            create={create}
-            edit={edit}
-            key={name}
-            list={list}
-            name={name}
-            options={{
-              api,
-              fieldFactory,
-              inputFactory,
-              resource,
-            }}
-            remove={remove}
-            show={show}
-          />
-        );
-      })}
+      {api.resources.map(resource =>
+        resourceFactory(resource, api, fieldFactory, inputFactory),
+      )}
     </Admin>
   );
 };
diff --git a/src/index.js b/src/index.js
index 5b59ba5..dc611c2 100644
--- a/src/index.js
+++ b/src/index.js
@@ -3,6 +3,7 @@ export Create from './Create';
 export Edit from './Edit';
 export fieldFactory from './fieldFactory';
 export inputFactory from './inputFactory';
+export resourceFactory from './resourceFactory';
 export List from './List';
 export Show from './Show';
 export * from './hydra';
diff --git a/src/resourceFactory.js b/src/resourceFactory.js
new file mode 100644
index 0000000..b8ea427
--- /dev/null
+++ b/src/resourceFactory.js
@@ -0,0 +1,36 @@
+import {Delete, Resource} from 'admin-on-rest';
+import React from 'react';
+import Create from './Create';
+import Edit from './Edit';
+import List from './List';
+import Show from './Show';
+
+export default (resource, api, fieldFactory, inputFactory) => {
+  const {
+    create = Create,
+    edit = Edit,
+    list = List,
+    name,
+    props,
+    remove = Delete,
+    show = Show,
+  } = resource;
+  return (
+    <Resource
+      {...props}
+      create={create}
+      edit={edit}
+      key={name}
+      list={list}
+      name={name}
+      options={{
+        api,
+        fieldFactory,
+        inputFactory,
+        resource,
+      }}
+      remove={remove}
+      show={show}
+    />
+  );
+};


Test patch:
diff --git a/src/resourceFactory.test.js b/src/resourceFactory.test.js
new file mode 100644
index 0000000..68637c5
--- /dev/null
+++ b/src/resourceFactory.test.js
@@ -0,0 +1,145 @@
+import {Resource} from 'admin-on-rest';
+import resourceFactory from './resourceFactory';
+
+describe('makes Resource component', () => {
+  const resource = {
+    name: 'name',
+    props: {custom: 'props'},
+  };
+  const api = {
+    name: 'api',
+  };
+
+  const fieldFactory = jest.fn();
+  const inputFactory = jest.fn();
+
+  describe('makes default Resource component', () => {
+    const resourceComponent = resourceFactory(
+      resource,
+      api,
+      fieldFactory,
+      inputFactory,
+    );
+
+    test('is Resource component', () => {
+      expect(resourceComponent.type.prototype).toEqual(Resource.prototype);
+    });
+
+    test('have props defined', () => {
+      expect(resourceComponent.props).toBeDefined();
+    });
+
+    test('have proper name prop set', () => {
+      expect(resourceComponent.props.name).toEqual(resource.name);
+    });
+
+    test('have default actions: create, edit, list, remove, show set', () => {
+      expect(resourceComponent.props.create).toBeInstanceOf(Function);
+      expect(resourceComponent.props.edit).toBeInstanceOf(Function);
+      expect(resourceComponent.props.list).toBeInstanceOf(Function);
+      expect(resourceComponent.props.remove).toBeInstanceOf(Function);
+      expect(resourceComponent.props.show).toBeInstanceOf(Function);
+    });
+
+    test('have proper custom props set', () => {
+      Object.keys(resource.props).forEach(key => {
+        expect(resourceComponent.props[key]).toEqual(resource.props[key]);
+      });
+    });
+
+    test('have proper options prop set', () => {
+      expect(resourceComponent.props.options).toEqual({
+        api,
+        fieldFactory,
+        inputFactory,
+        resource,
+      });
+    });
+  });
+
+  describe('makes Resource component with custom actions', () => {
+    test('have custom create action', () => {
+      const customCreate = jest.fn();
+      const resourceComponent = resourceFactory(
+        Object.assign({}, resource, {
+          create: customCreate,
+        }),
+        api,
+        fieldFactory,
+        inputFactory,
+      );
+
+      expect(resourceComponent.props.create).toEqual(customCreate);
+    });
+
+    test('have custom create action', () => {
+      const customCreate = jest.fn();
+      const resourceComponent = resourceFactory(
+        Object.assign({}, resource, {
+          create: customCreate,
+        }),
+        api,
+        fieldFactory,
+        inputFactory,
+      );
+
+      expect(resourceComponent.props.create).toEqual(customCreate);
+    });
+
+    test('have custom list action', () => {
+      const customList = jest.fn();
+      const resourceComponent = resourceFactory(
+        Object.assign({}, resource, {
+          list: customList,
+        }),
+        api,
+        fieldFactory,
+        inputFactory,
+      );
+
+      expect(resourceComponent.props.list).toEqual(customList);
+    });
+
+    test('have custom show action', () => {
+      const customShow = jest.fn();
+      const resourceComponent = resourceFactory(
+        Object.assign({}, resource, {
+          show: customShow,
+        }),
+        api,
+        fieldFactory,
+        inputFactory,
+      );
+
+      expect(resourceComponent.props.show).toEqual(customShow);
+    });
+
+    test('have custom remove action', () => {
+      const customDelete = jest.fn();
+      const resourceComponent = resourceFactory(
+        Object.assign({}, resource, {
+          remove: customDelete,
+        }),
+        api,
+        fieldFactory,
+        inputFactory,
+      );
+
+      expect(resourceComponent.props.remove).toEqual(customDelete);
+    });
+
+    test('have custom edit action', () => {
+      const customEdit = jest.fn();
+      const resourceComponent = resourceFactory(
+        Object.assign({}, resource, {
+          edit: customEdit,
+        }),
+        api,
+        fieldFactory,
+        inputFactory,
+      );
+
+      expect(resourceComponent.props.edit).toEqual(customEdit);
+    });
+  });
+});