MLyck
MLyck

Reputation: 5765

react-admin: SimpleForm in Isolation

Being a big fan of Storybook I like to test my components in Isolation.

Especially Forms.

However, I'm having a very hard time figuring out how the heck I can get my forms or even a single form field to work in isolation. (without Edit, Create, Resource and Admin wrapping everything).

What I basically want is just to render a SimpleForm component with a single Input inside to prototype each of my inputs. (I use custom inputs as I don't like Material-ui)

My inputs are decorated with addField from ra-core like this textInput example:

import React from 'react';
import { addField } from 'react-admin';
import sanitizeRestProps from './sanitizeRestProps';
import { Input } from 'antd';
import Labeled from './Labeled';

const TextInput = ({ id, label, labelPosition, input, isRequired, ...rest }) => (
    <Labeled label={label} position={labelPosition} id={id} isRequired={isRequired}>
        <Input id={id} {...input} {...sanitizeRestProps(rest)} />
    </Labeled>
);

export default addField(TextInput);

If I try to render this Input on it's own I get the error "Field must be inside a component decorated with reduxForm()"

Which is fair... I want to test that it "works" as well when I'm building it in a component, so I added a SimpleForm wrapper.

Like so:

return (
        <SimpleForm>
            <TextInput label={label} source="test" />
        </SimpleForm>
    );

The component now renders, but I can't edit it at all. The "onChange" function from the input prop that it gets through SimpleForm and addField decorator does get called. But the value never changes.

I then tried to mock every prop this SimpleForm requires like this:

return (
        <SimpleForm
            resource="test"
            record={{ id: 1, test: 'blah' }}
            initialValues={{ test: 'test2' }}
            save={console.log}
            basePath="/test"
            saving={false}
            submitOnEnter={false}
            undoable={false}
            validate={() => true}
            version={1}
        >
            <TextInput label={label} source="test" />
        </SimpleForm>
    );

But my input is still just empty, I can't edit it, and it doesn't get the initialValue or default value I set in the record...

It seems ridicolous if this REALLY needs a Resource, Edit and Admin components JUST to render a single form field.

Note (it's not my field that's broken) When I load up the full site where IT IS integrated with all the Admin, Resources, Edit, Create etc. These fields work just fine.

But it's a massive pain having to load the website, login, navigate etc. EVERYTIME I'm making small changes to a single Input component.

How can I mock that environment so I can test forms in isolation???

I'm using react-admin 2.8.5 (before they switched to react-final-form)'

Upvotes: 5

Views: 2337

Answers (1)

gstvg
gstvg

Reputation: 927

Edit: Simply wrap the SimpleForm with a react-redux Provider with the store that react-admin and redux-form components expects. Tested with 2.8.5

import React from "react";
import { render } from "react-dom";
import { SimpleForm, TextInput } from "react-admin";
import { Provider } from "react-redux";
import { createStore, combineReducers } from "redux";
import { reducer as formReducer } from "redux-form";

const reducers = {
  admin: (state = {}) => state,
  form: formReducer
};

const store = createStore(combineReducers(reducers));

const App = () => (
  <Provider store={store}>
    <SimpleForm record={{ test: "test2" }}>
      <TextInput label="label" source="test" />
    </SimpleForm>
  </Provider>
);

render(<App />, document.getElementById("root"));

Test it here:

https://codesandbox.io/s/react-final-form-submission-errors-example-jfd5k?file=/index.js:0-647

Old answer:

The minimal setup i find that works is this, it will display no layout, and will redirect directly to the input:

import { Admin, Resource, Layout, SimpleForm, TextInput } from 'react-admin'

const NoLayout = props => (
    <Layout
        appBar={"span"}
        sideBar={"span"}
        menu={"span"}
        {...props}
    />
)

const Dashboard = props => (
    <SimpleForm>
        <TextInput source="test" label="Source Name"}/>
    </SimpleForm>
)

const App = () => {

    return (
        <Admin dataProvider={() => {}} menu={"span"} dashboard={Dashboard} layout={NoLayout}>
            <Resource name="test"/>
        </Admin>
    )
}

You don't need a real dataProvider. The Resource is for react admin not complaining about an empty Admin

Upvotes: 2

Related Questions