New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Customize CRUD components #63

Merged
merged 1 commit into from Nov 2, 2017

Conversation

Projects
None yet
8 participants
@mauchede
Member

mauchede commented Oct 18, 2017

Q A
Bug fix? no
New feature? yes
BC breaks? yes (remove fieldComponent and inputComponent)
Deprecations? no
Tests pass? yes
Fixed tickets #38
License MIT
Doc PR not yet

Note: PR #7 should be useless after merging this PR.


With this PR, we give the possibility to users to customize:

  • the components used by Resource (Create, Edit, List, Remove and Show).
  • the factories (fieldFactory and inputFactory).

To change a CRUD component, we just have to define them in api:

  • api.resources[key of the resource].create to change Create.
  • api.resources[key of the resource].edit to change Edit.
  • api.resources[key of the resource].list to change List.
  • api.resources[key of the resource].remove to change Remove.
  • api.resources[key of the resource].show to change Show.

I removed fieldComponent and inputComponent. IMO:

  • the suffix Component is incorrect because there are not components.
  • all customization in this project can be done by component (CRUD component, HydraAdmin::error and HydraAdmin::loading).

Here an example of usage:

import parseHydraDocumentation from '@api-platform/api-doc-parser/lib/hydra/parseHydraDocumentation';
import { Datagrid, EditButton, ImageField, List, ShowButton, TextField } from 'admin-on-rest';
import React from 'react';
import SingleImageInput from '../components/inputs/single-image-input';

export default entrypoint => parseHydraDocumentation(entrypoint)
    .then(
        ({ api }) => {

            // Customize "Gallery" resource

            const gallery = api.resources.find(({ name }) => 'galleries' === name);

            gallery.list = (props) => (
                <List {...props}>
                    <Datagrid>
                        <TextField source="id"/>
                        {props.options.fieldFactory(props.options.resource.fields.find(({ name }) => name === 'name'))}
                        {props.options.fieldFactory(props.options.resource.fields.find(({ name }) => name === 'mainImage'))}
                        {props.hasShow && <ShowButton />}
                        {props.hasEdit && <EditButton />}
                    </Datagrid>
                </List>
            );

            // Customize "images" field

            const images = gallery.fields.find(({ name }) => 'images' === name);

            images.field = props => (
                <ImageField {...props} src="contentUrl"/>
            );

            images.input = props => (
                <SingleImageInput {...props} accept="image/*" multiple={true}>
                    <ImageField source="contentUrl"/>
                </SingleImageInput>
            );

            images.input.defaultProps = {
                addField: true,
                addLabel: true,
            };

            // Customize "mainImage" field

            const mainImage = gallery.fields.find(({ name }) => 'mainImage' === name);

            mainImage.field = props => (
                <ImageField {...props} source={`${props.source}.contentUrl`}/>
            );

            mainImage.input = props => (
                <SingleImageInput {...props} accept="image/*" multiple={false}>
                    <ImageField source="contentUrl"/>
                </SingleImageInput>
            );

            mainImage.input.defaultProps = {
                addField: true,
                addLabel: true,
            };

            mainImage.input.defaultProps = {
                addField: true,
                addLabel: true,
            };

            return { api };
        },
    );

To change fieldComponent or inputComponent, we just have to define them in HydraAdmin:

import { HydraAdmin } from '@api-platform/admin';
import React from 'react';

export default props => (
    <HydraAdmin
        entrypoint='http://localhost:8000'
        fieldFactory={(field) => {
            // return an field
        }}
        inputFactory={(field) => {
            // return an input
        }}
    />
);

I removed usage of inputProps and fieldProps: if the user wants to change props, he / she has to define the component in api.resources[...].fields[key of the field].input our api.resources[...].fields[key of the field].field.

@meyerbaptiste

This comment has been minimized.

Show comment
Hide comment
@meyerbaptiste

meyerbaptiste Oct 18, 2017

Member

This is amazing! 👍

Member

meyerbaptiste commented Oct 18, 2017

This is amazing! 👍

@dunglas

This is huge

@Gregcop1

Small coms but it's a really good job.
BTW, in your example, sometimes you use today style and sometimes not 😋

Show outdated Hide outdated src/AdminBuilder.js Outdated
Show outdated Hide outdated src/AdminBuilder.js Outdated
Show outdated Hide outdated src/AdminBuilder.js Outdated
Show outdated Hide outdated src/AdminBuilder.js Outdated
Show outdated Hide outdated src/List.js Outdated
@mauchede

This comment has been minimized.

Show comment
Hide comment
@mauchede

mauchede Oct 19, 2017

Member

@Gregcop1: I applied your fixes. Now all CRUD components are stateless.

Member

mauchede commented Oct 19, 2017

@Gregcop1: I applied your fixes. Now all CRUD components are stateless.

edit={Edit}
remove={Delete}
{...resource.props}
{...props}

This comment has been minimized.

@Gregcop1

Gregcop1 Oct 19, 2017

I'm not sure but I think you can

{
  ...props, 
  create, 
  edit,
  remove,
  show,
  list,
  name
}
@Gregcop1

Gregcop1 Oct 19, 2017

I'm not sure but I think you can

{
  ...props, 
  create, 
  edit,
  remove,
  show,
  list,
  name
}

This comment has been minimized.

@mauchede

mauchede Oct 19, 2017

Member

It does not work but the following syntax works:

            {...{
              ...props,
              create,
              edit,
              key: name,
              list,
              name,
              options: {
                api,
                fieldFactory,
                inputFactory,
                resource,
              },
              remove,
              show,
            }}

Do you prefer this syntax?

@mauchede

mauchede Oct 19, 2017

Member

It does not work but the following syntax works:

            {...{
              ...props,
              create,
              edit,
              key: name,
              list,
              name,
              options: {
                api,
                fieldFactory,
                inputFactory,
                resource,
              },
              remove,
              show,
            }}

Do you prefer this syntax?

This comment has been minimized.

@Gregcop1
@Gregcop1

Gregcop1 Oct 19, 2017

nope ^^

This comment has been minimized.

@toby-griffiths

toby-griffiths Nov 2, 2017

I think it’s better than the above as it’s more explicit, and therefore more readable.

@toby-griffiths

toby-griffiths Nov 2, 2017

I think it’s better than the above as it’s more explicit, and therefore more readable.

? this.props.options.fieldFactory
: fieldFactory;
const Show = props => {
const {options: {api, fieldFactory, resource}} = props;

This comment has been minimized.

@Gregcop1

Gregcop1 Oct 19, 2017

disgusting :p

@Gregcop1

Gregcop1 Oct 19, 2017

disgusting :p

This comment has been minimized.

@mauchede

mauchede Oct 19, 2017

Member

Ha ha nested object destructuring is a "today" javascript!

@mauchede

mauchede Oct 19, 2017

Member

Ha ha nested object destructuring is a "today" javascript!

This comment has been minimized.

@ginifizz

ginifizz Oct 19, 2017

No. It's not.

@ginifizz

ginifizz Oct 19, 2017

No. It's not.

This comment has been minimized.

@soyuka

soyuka Oct 19, 2017

Member

So is the spread operator then.

@soyuka

soyuka Oct 19, 2017

Member

So is the spread operator then.

@Gregcop1

This comment has been minimized.

Show comment
Hide comment
@Gregcop1

Gregcop1 commented Oct 19, 2017

👍

@dunglas dunglas merged commit d7545cb into api-platform:master Nov 2, 2017

1 check passed

continuous-integration/travis-ci/pr The Travis CI build passed
Details
@dunglas

This comment has been minimized.

Show comment
Hide comment
@dunglas

dunglas Nov 2, 2017

Member

Thanks @mauchede

Member

dunglas commented Nov 2, 2017

Thanks @mauchede

@thecassion

This comment has been minimized.

Show comment
Hide comment
@thecassion

thecassion Jun 12, 2018

This change made nothing for me . I can't customize with a component like SelectArrayInput . I ask everywhere but I can't have any responses .

thecassion commented Jun 12, 2018

This change made nothing for me . I can't customize with a component like SelectArrayInput . I ask everywhere but I can't have any responses .

@mauchede

This comment has been minimized.

Show comment
Hide comment
@mauchede

mauchede Jun 13, 2018

Member

Hi @thecassion,

I already used theses changes on some projects. Without information, it will be difficult to help you. Could you open an issue with some examples?

Member

mauchede commented Jun 13, 2018

Hi @thecassion,

I already used theses changes on some projects. Without information, it will be difficult to help you. Could you open an issue with some examples?

@thecassion

This comment has been minimized.

Show comment
Hide comment
@thecassion

thecassion Jun 14, 2018

Hi @mauchede .
It's ok right now . I've updated my api-platform/admin that use react-admin instead of admin-on-rest. I think that we should update the documentation on api-platform website.

thecassion commented Jun 14, 2018

Hi @mauchede .
It's ok right now . I've updated my api-platform/admin that use react-admin instead of admin-on-rest. I think that we should update the documentation on api-platform website.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment