npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

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

npm npm npm

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.