Add ViewRegistry for view selection and ViewProvider
authorHugo Delahousse <hdelahousse@logilab.fr>
Wed, 14 Jun 2017 11:06:21 +0200
changeset 231 f811f0442a5c
parent 230 737cba398b88
child 232 f8c873a819d4
Add ViewRegistry for view selection and ViewProvider The ViewProvider (GenericViewProvider) provides views to the ViewRegistry The existing Views' props have been normalized to take a 'data' property, typed as JsonaryWrapper
src/components/Resource.js
src/services/viewregistry.js
src/views/Generics.js
test/index.js
--- a/src/components/Resource.js	Fri Jun 16 10:43:52 2017 +0200
+++ b/src/components/Resource.js	Wed Jun 14 11:06:21 2017 +0200
@@ -9,7 +9,7 @@
 import {PropTypesResourceModel} from '../model';
 import {mapToSchema} from '../jsonaryutils';
 import {ActionsDropDown, withPagination} from './BaseViews';
-import {ResourceCollectionView, ResourceEntityView} from '../views/Generics.js';
+import viewRegistry from '../services/viewregistry';
 
 export class ResourceContainer extends React.Component {
     constructor(props) {
@@ -120,10 +120,12 @@
 export class ResourceView extends React.Component {
     constructor(props) {
         super(props);
+        this.view = viewRegistry.selectBestView(props.resource.data);
     }
 
     render() {
         const data = this.props.resource.data;
+        const View = this.view;
         const upLink = data.getLink('up');
 
         let breadcrumb = null;
@@ -152,15 +154,10 @@
             </div>
         );
 
-        const isCollection = data.schemas().basicTypes()[0] === 'array';
-        let actualView = <ResourceEntityView entity={data} />;
-        if (isCollection) {
-            actualView = <ResourceCollectionView resource={this.props.resource} />;
-        }
         return (
             <div>
                 { header }
-                { actualView }
+                { <View data={data} /> }
             </div>
         );
     }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/services/viewregistry.js	Wed Jun 14 11:06:21 2017 +0200
@@ -0,0 +1,29 @@
+import {GenericViewProvider} from '../views/Generics.js';
+
+export class ViewRegistry {
+    constructor() {
+        this.viewProviders = [];
+        this.addViewProvider(new GenericViewProvider());
+    }
+
+    addViewProvider(viewProvider) {
+        this.viewProviders.push(viewProvider);
+    }
+
+    selectBestView(data) {
+        let bestScore = 0;
+        let bestView = null;
+        for (const provider of this.viewProviders) {
+            const selectedViews = provider.select(data);
+            for (const match of selectedViews) {
+                if (match.score > bestScore) {
+                    bestScore = match.score;
+                    bestView = match.view;
+                }
+            }
+        }
+        return bestView;
+    }
+}
+
+export default new ViewRegistry();
--- a/src/views/Generics.js	Fri Jun 16 10:43:52 2017 +0200
+++ b/src/views/Generics.js	Wed Jun 14 11:06:21 2017 +0200
@@ -6,9 +6,22 @@
     RelatedResources} from '../components/Entity';
 import {CollectionView} from '../components/BaseViews';
 
-export function ResourceCollectionView({resource}) {
-    if (resource.data.length() > 0) {
-        return <CollectionView collection={resource.data} />;
+export class GenericViewProvider {
+    select(data) {
+        const match = {score: 1};
+        const isCollection = data.schemas().basicTypes()[0] === 'array';
+        if (isCollection) {
+            match.view = ResourceCollectionView;
+        } else {
+            match.view = ResourceEntityView;
+        }
+        return [match];
+    }
+}
+
+export function ResourceCollectionView({data}) {
+    if (data.length() > 0) {
+        return <CollectionView collection={data} />;
     }
 
     return <div className="text-mutted">
@@ -16,12 +29,12 @@
     </div>;
 }
 ResourceCollectionView.propTypes = {
-    resource: PropTypeJsonaryWrapper.isRequired,
+    data: PropTypeJsonaryWrapper.isRequired,
 };
 
-export function ResourceEntityView({entity}) {
+export function ResourceEntityView({data}) {
     function renderRelated() {
-        const links = entity.links('related');
+        const links = data.links('related');
         if (links.length > 0) {
             return Array.prototype.map.call(links, (link) => {
                 return (<ResourceContainer url={link.href}
@@ -34,12 +47,12 @@
     }
     return (
         <div>
-            <EntityAttributes data={entity} />
-            <EntityMeta data={entity} />
+            <EntityAttributes data={data} />
+            <EntityMeta data={data} />
             { renderRelated() }
         </div>
     );
 }
 ResourceEntityView.propTypes = {
-    entity: PropTypeJsonaryWrapper.isRequired,
+    data: PropTypeJsonaryWrapper.isRequired,
 };
--- a/test/index.js	Fri Jun 16 10:43:52 2017 +0200
+++ b/test/index.js	Wed Jun 14 11:06:21 2017 +0200
@@ -817,7 +817,7 @@
             const resource = {data: mapToSchema([], {type: 'array'})};
             const wrapper = shallow(<ResourceView resource={resource}/>);
 
-            const view = <ResourceCollectionView resource={resource} />;
+            const view = <ResourceCollectionView data={resource.data} />;
             expect(wrapper.contains(view)).to.be.true;
         });
     });
@@ -827,7 +827,7 @@
             const resource = {data: mapToSchema({}, {type: 'object'})};
             const wrapper = shallow(<ResourceView resource={resource}/>);
 
-            const view = <ResourceEntityView entity={resource.data} />;
+            const view = <ResourceEntityView data={resource.data} />;
             expect(wrapper.contains(view)).to.be.true;
         });
     });