ra-component-factory
v0.6.0
Published
[![npm](https://img.shields.io/npm/dw/ra-component-factory.svg)](https://www.npmjs.com/package/ra-component-factory) [![npm](https://img.shields.io/npm/v/ra-component-factory.svg)](https://www.npmjs.com/package/ra-component-factory) [![npm](https://img.sh
Downloads
19
Readme
ra-component-factory (former: aor-component-factory)
react-admin component-factory when used with http://github.com/marmelab/react-admin provides a centralized way to easily configure:
- permissions on action buttons (should a CreateButton be visible ?)
- permissions on Forms/Actions (should edit form of a resource be accessible to a user?)
- permissions on Menu Links (should a Menu Link be visible ?)
- permissions on front-end visibility/immutability (should a property be visible / readonly ?)
- reordering of elements in the views
- handling of tabbed views/forms
- (soon) mobile responsive view integration
Versions
Download version ra-component-factory 0.3.0 for admin-on-rest 1.4.x
Download version ra-component-factory 0.4.0 for react-admin 2.2.x
Download version ra-component-factory 0.6.x for react-admin 3.x.x
Installation:
npm install ra-component-factory
Global Config:
Add a global Config like that (e.g config/factoryConfig.js):
import postsConfig from './config/postsConfig';
export default {
resources: {
posts: postsConfig
},
roleEntryInLocalStorage: 'user_role',
tabDelimiter: '-----TAB-----',
readOnlyPrefix: '_'
}
Add a config for each of the resources you want to use the factory with (e.g config/postsConfig.js). You can have all resources in the same file if you choose so. Assuming you have two roles (role1 and role2) in your app and the role of the user is found in the local Storage at user_role
it will look like that:
import React from 'react';
import {
TextInput,
TextField,
DateInput,
DateField,
ReferenceInput,
ReferenceField,
ChipField
} from 'react-admin';
export default {
props: {
id: {
input: (<TextInput source="id"/>),
field: (<TextField source="id"/>),
},
name: {
input: (<TextInput label="Name" source="name"/>),
field: (<TextField label="Name" source="name"/>),
},
date: {
input: (<DateInput source="date" parse={dateParser} label="Post Date"/>),
field: (<DateField source="date" type="date" label="Post Date"/>),
},
dateGte: { //date Greater than equal
input: (<DateInput source="dateGte" parse={dateParser} label="Date from"/>),
},
dateLte: { // date Less than equal
input: (<DateInput source="dateLte" parse={dateParser} label="Date to"/>),
},
author: {
input: <ReferenceInput label="Author" source="author" reference="authors" allowEmpty>
<SelectInput optionText="name" translate={false}/>
</ReferenceInput>,
field: <ReferenceField label="Author" source="author" reference="authors" sortable={false} linkType={false} allowEmpty={true}>
<ChipField source="name"/>
</ReferenceField>
},
},
role1: {
create: {
props: ["name", "author", "date"],
action: true
},
edit: {
props: ["_id", "name", "author", "date"],
action: true
},
list: {
props: ["id", "name", "author", "date"],
action: true
},
filter: {
props: ["q", "id", "author", "dateGte", "dateLte"],
action: true
},
show: {
props: ["id", "name", "author"],
action: true
},
search: {
action: true
},
delete: {
action: true
},
},
role2: {
create: {
props: [],
action: false
},
edit: {
props: [],
action: false
},
list: {
props: ["id", "name", "author", "date"],
action: false
},
filter: {
props: ["q", "id", "author", "dateGte", "dateLte"],
action: true
},
show: {
props: ["id", "name", "author"],
action: true
},
search: {
action: true
},
delete: {
action: false
},
}
};
import Factory from 'ra-component-factory';
import factoryConfig from './config/factoryConfig';
const factory = new Factory("posts", factoryConfig);
Usage in Create, List, Edit, Filter and Show:
Separate fields:
<Edit {...props}>
<SimpleForm>
{factory.create("edit","id")} // this will be readonly since in edit it is denoted as _id
{factory.create("edit","name")}
{factory.create("edit","author")}
</SimpleForm>
</Edit>
Creation of fields all at once - based on the order of the configuration
<Edit title={<CompanyTitle />} {...props}>
<SimpleForm>
{factory.createAll("edit")}
</SimpleForm>
</Edit>
Usage in Menu:
{new Factory("posts", factoryConfig).canSeeMenuLink() &&
<MenuItemLink
key="posts"
to={`/posts`}
primaryText={translate(`resources.posts.name`, { smart_count: 2 })}
leftIcon={<PostIcon color="#fff" />}
onClick={onMenuTap}
style={{color: "#fff"}}
/>}
Rendering Action buttons (Edit/Delete/Create/Show/List) based on roles:
createCreateButton(basePath) will return empty for Create Button if the user with role1 doesn't have create { props: [...], action: true }
in the configuration. We need to provide custom Actions in each of the components Show/Edit/List/Create to control which buttons are rendered based on roles. Note in List we don't need to provide basePath/data in the createXYZButton methods. This information is passed by their parents.
e.g for CreateButton:
const ListActions = ({ permissions, resource, filters, displayedFilters, filterValues, basePath, showFilter, refresh }) => (
<CardActions style={cardActionStyle}>
{filters && factory.canFilter() && React.cloneElement(filters, { resource, showFilter, displayedFilters, filterValues, context: 'button' }) }
{factory.createCreateButton(basePath)}
<FlatButton primary label="refresh" onClick={refresh} icon={<NavigationRefresh />} />
</CardActions>
);
const ShowActions = ({ resource, filters, displayedFilters, filterValues, data, basePath, showFilter, refresh }) => (
<CardActions style={cardActionStyle}>
{factory.createEditButton(basePath, data)}
{factory.createListButton(basePath)}
{factory.createDeleteButton(basePath, data)}
<FlatButton primary label="refresh" onClick={refresh} icon={<NavigationRefresh />} />
</CardActions>
);
const EditActions = ({ resource, filters, displayedFilters, filterValues, basePath, data, showFilter, refresh }) => (
<CardActions style={cardActionStyle}>
{factory.createShowButton(basePath, data)}w
{factory.createListButton(basePath)}
{factory.createDeleteButton(basePath, data)}
<FlatButton primary label="refresh" onClick={refresh} icon={<NavigationRefresh />} />
</CardActions>
);
export const PostList = (props) => (
<List title="All posts" {...props} filters={<PostFilter/>} actions={<ListActions />} sort={{field: 'id', order: 'DESC'}} perPage={5}>
<Datagrid>
{factory.createAll("list")}
{factory.createShowButton()}
{factory.createEditButton()}
{factory.createDeleteButton()}
</Datagrid>
</List>
);
export const PostEdit = (props) => (
<Edit actions={<EditActions/>} title={<PostTitle />} {...props}>
<SimpleForm redirect={false}>
{factory.createAll("edit")}
</SimpleForm>
</Edit>
);
export const PostCreate = (props) => (
<Create {...props}>
<SimpleForm redirect="list">
{factory.createAll("create")}
</SimpleForm>
</Create>
);
export const PostShow = (props) => (
<Show actions={<ShowActions/>} {...props}>
<SimpleShowLayout>
{factory.createAll("show")}
</SimpleShowLayout>
</Show>
);
Hide properties
If you want to hide a property of a resource from list for a secific role (e.g role1) you just remove it from its internal props
array. The same can be done for create, edit and filter.
Readonly properties in Edit Mode:
if you want a property to be readonly in Edit Mode you prefix it with "_" or whatever prefix you have configured at factoryConfig.readOnlyPrefix
Global Text Search q
if you want to have Search in all fields of a resource, you just add "q" in the filter props
Tabs
if you want to have tabbed forms Edit
/Create
or tabbed Show
layout you add tab delimiters '-----TAB-----'
or whatever is configured in factoryConfig.tabDelimiter
as a property in actions create, edit or show.
{
role1: {
create: {
props: ["name", "author", "-----TAB-----", "date"],
tabs: ["Sample Tab 1", "Sample Tab 2"],
action: true
},
[...]
},
role2: {
create: {
props: ["name", "-----TAB-----", "author", "-----TAB-----", "date"],
tabs: ["Sample Tab 1", "Sample Tab 2", "Sample Tab 3"],
action: true
},
[...]
},
[...]
}
<Create {...props}>
<TabbedForm>
{factory.createAll("create")}
</TabbedForm>
</Create>
This will put inputs name
and author
in the first Tab
and date
in the second FormTab
for role1
users.
It will put input name
in the first FormTab
, author
in the second FormTab
and date
in the third FormTab
for role2
users.
if tabs is missing from the configuration - the default labels "Tab 1", "Tab 2", "Tab 3" will show up
Similarly for Show:
<Show {...props}>
<TabbedShowLayout>
{factory.createAll("show")}
</TabbedShowLayout>
</Show>
Make sure you either have tabs for a specific action (factory call is in TabbedForm
or TabbedShowLayout
component) for all roles or you don't
In case a role doesn't need Tabs you need to at least have one "dummy/empty title" tab which can be configured like this:
role2: {
create: {
props: ["name", "author", "date", "-----TAB-----"],
tabs: [""],
action: true
},
[...]
},
Responsive configuration
Coming soon
License
This translation is licensed under the MIT Licence.