From b1492184cfaefe3f523ac9cee1404d94541072e2 Mon Sep 17 00:00:00 2001 From: Gildas Garcia <1122076+djhi@users.noreply.github.com> Date: Wed, 15 Sep 2021 11:41:08 +0200 Subject: [PATCH] Add new providers and inputs to documentation --- docs/DataProviders.md | 291 +++++----- docs/Inputs.md | 1267 ++++++++++++++++++++++++++--------------- 2 files changed, 942 insertions(+), 616 deletions(-) diff --git a/docs/DataProviders.md b/docs/DataProviders.md index 8807b5327cc..3761f2bff96 100644 --- a/docs/DataProviders.md +++ b/docs/DataProviders.md @@ -1,6 +1,6 @@ --- layout: default -title: "Data Providers" +title: 'Data Providers' --- # Data Providers @@ -8,14 +8,12 @@ title: "Data Providers" Whenever react-admin needs to communicate with the API, it calls methods on the Data Provider object. ```js -dataProvider - .getOne('posts', { id: 123 }) - .then(response => { - console.log(response.data); // { id: 123, title: "hello, world" } - }); +dataProvider.getOne('posts', { id: 123 }).then(response => { + console.log(response.data); // { id: 123, title: "hello, world" } +}); ``` -It's the Data Provider's job to turn these method calls into HTTP requests, and transform the HTTP responses to the data format expected by react-admin. In technical terms, a Data Provider is an *adapter* for an API. +It's the Data Provider's job to turn these method calls into HTTP requests, and transform the HTTP responses to the data format expected by react-admin. In technical terms, a Data Provider is an _adapter_ for an API. And to inject a Data Provider in a react-admin application, pass it as the `dataProvider` prop of the `` component, as follows: @@ -23,11 +21,7 @@ And to inject a Data Provider in a react-admin application, pass it as the `data import { Admin, Resource } from 'react-admin'; import dataProvider from '../myDataProvider'; -const App = () => ( - - // ... - -) +const App = () => // ...; ``` Thanks to this adapter injection system, react-admin can communicate with any API, whether it uses REST, GraphQL, RPC, or even SOAP, regardless of the dialect it uses. The Data Provider is also the ideal place to add custom HTTP headers, authentication, etc. @@ -38,16 +32,16 @@ A Data Provider must have the following methods: ```jsx const dataProvider = { - getList: (resource, params) => Promise, - getOne: (resource, params) => Promise, - getMany: (resource, params) => Promise, + getList: (resource, params) => Promise, + getOne: (resource, params) => Promise, + getMany: (resource, params) => Promise, getManyReference: (resource, params) => Promise, - create: (resource, params) => Promise, - update: (resource, params) => Promise, + create: (resource, params) => Promise, + update: (resource, params) => Promise, updateMany: (resource, params) => Promise, - delete: (resource, params) => Promise, + delete: (resource, params) => Promise, deleteMany: (resource, params) => Promise, -} +}; ``` You can find an example Data Provider implementation at the end of this chapter. @@ -60,61 +54,63 @@ You can find an example Data Provider implementation at the end of this chapter. The react-admin project includes 5 Data Providers: -* Simple REST: [marmelab/ra-data-simple-rest](https://github.com/marmelab/react-admin/tree/master/packages/ra-data-simple-rest) ([read more below](#usage)). It serves mostly as an example. Incidentally, it is compatible with the [FakeRest](https://github.com/marmelab/FakeRest) API. -* **[JSON server](https://github.com/typicode/json-server)**: [marmelab/ra-data-json-server](https://github.com/marmelab/react-admin/tree/master/packages/ra-data-json-server). Great for prototyping an admin over a yet-to-be-developed REST API. -* [Simple GraphQL](https://graphql.org/): [marmelab/ra-data-graphql-simple](https://github.com/marmelab/react-admin/tree/master/packages/ra-data-graphql-simple). A GraphQL provider built with Apollo and tailored to target a simple GraphQL implementation. -* Local JSON: [marmelab/ra-data-fakerest](https://github.com/marmelab/react-admin/tree/master/packages/ra-data-fakerest). Based on a local object, it doesn't even use HTTP. Use it for testing purposes. -* Local Storage: [marmelab/ra-data-localstorage](https://github.com/marmelab/react-admin/tree/master/packages/ra-data-localstorage). User editions are persisted across refreshes and between sessions. This allows local-first apps, and can be useful in tests. +- Simple REST: [marmelab/ra-data-simple-rest](https://github.com/marmelab/react-admin/tree/master/packages/ra-data-simple-rest) ([read more below](#usage)). It serves mostly as an example. Incidentally, it is compatible with the [FakeRest](https://github.com/marmelab/FakeRest) API. +- **[JSON server](https://github.com/typicode/json-server)**: [marmelab/ra-data-json-server](https://github.com/marmelab/react-admin/tree/master/packages/ra-data-json-server). Great for prototyping an admin over a yet-to-be-developed REST API. +- [Simple GraphQL](https://graphql.org/): [marmelab/ra-data-graphql-simple](https://github.com/marmelab/react-admin/tree/master/packages/ra-data-graphql-simple). A GraphQL provider built with Apollo and tailored to target a simple GraphQL implementation. +- Local JSON: [marmelab/ra-data-fakerest](https://github.com/marmelab/react-admin/tree/master/packages/ra-data-fakerest). Based on a local object, it doesn't even use HTTP. Use it for testing purposes. +- Local Storage: [marmelab/ra-data-localstorage](https://github.com/marmelab/react-admin/tree/master/packages/ra-data-localstorage). User editions are persisted across refreshes and between sessions. This allows local-first apps, and can be useful in tests. +- **[Supabase](https://supabase.io/)**: [marmelab/ra-supabase](https://github.com/marmelab/ra-supabase). Developers from the react-admin community have open-sourced Data Providers for many more backends: -* **[AWS Amplify](https://docs.amplify.aws)**: [MrHertal/react-admin-amplify](https://github.com/MrHertal/react-admin-amplify) -* **[Configurable Identity Property REST Client](https://github.com/zachrybaker/ra-data-rest-client)**: [zachrybaker/ra-data-rest-client](https://github.com/zachrybaker/ra-data-rest-client) -* **[coreBOS](https://corebos.com/)**: [React-Admin coreBOS Integration](https://github.com/coreBOS/reactadminportal) -* **[Django Rest Framework](https://www.django-rest-framework.org/)**: [bmihelac/ra-data-django-rest-framework](https://github.com/bmihelac/ra-data-django-rest-framework) -* **[Eve](https://docs.python-eve.org/en/stable/)**: [smeng9/ra-data-eve](https://github.com/smeng9/ra-data-eve) -* **[Express & Sequelize](https://github.com/lalalilo/express-sequelize-crud)**: [express-sequelize-crud](https://github.com/lalalilo/express-sequelize-crud) -* **[Feathersjs](https://www.feathersjs.com/)**: [josx/ra-data-feathers](https://github.com/josx/ra-data-feathers) -* **[Firebase Firestore](https://firebase.google.com/docs/firestore)**: [benwinding/react-admin-firebase](https://github.com/benwinding/react-admin-firebase). -* **[Firebase Realtime Database](https://firebase.google.com/docs/database)**: [aymendhaya/ra-data-firebase-client](https://github.com/aymendhaya/ra-data-firebase-client). -* **[Google Sheets](https://www.google.com/sheets/about/)**: [marmelab/ra-data-google-sheets](https://github.com/marmelab/ra-data-google-sheets) -* **[GraphQL](https://graphql.org/)**: [marmelab/ra-data-graphql](https://github.com/marmelab/react-admin/tree/master/packages/ra-data-graphql) (uses [Apollo](https://www.apollodata.com/)) -* **[HAL](http://stateless.co/hal_specification.html)**: [b-social/ra-data-hal](https://github.com/b-social/ra-data-hal) -* **[Hasura](https://github.com/hasura/graphql-engine)**: [hasura/ra-data-hasura](https://github.com/hasura/ra-data-hasura), auto generates valid GraphQL queries based on the properties exposed by the Hasura API. -* **[Hydra](https://www.hydra-cg.com/) / [JSON-LD](https://json-ld.org/)**: [api-platform/admin/hydra](https://github.com/api-platform/admin/blob/master/src/hydra/dataProvider.js) -* **[IndexedDB](https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API)**: [tykoth/ra-data-dexie](https://github.com/tykoth/ra-data-dexie) -* **[JSON API](https://jsonapi.org/)**: [henvo/ra-jsonapi-client](https://github.com/henvo/ra-jsonapi-client) -* **[JSON HAL](https://tools.ietf.org/html/draft-kelly-json-hal-08)**: [ra-data-json-hal](https://www.npmjs.com/package/ra-data-json-hal) -* **[JSON server](https://github.com/typicode/json-server)**: [marmelab/ra-data-json-server](https://github.com/marmelab/react-admin/tree/master/packages/ra-data-json-server). -* **[LocalStorage](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage)**: [marmelab/ra-data-localstorage](https://github.com/marmelab/react-admin/tree/master/packages/ra-data-localstorage) -* **[Loopback3](https://loopback.io/lb3)**: [darthwesker/react-admin-loopback](https://github.com/darthwesker/react-admin-loopback) -* **[Loopback4](https://loopback.io/)**: [elmaistrenko/react-admin-lb4](https://github.com/elmaistrenko/react-admin-lb4) -* **[Loopback4 CRUD](https://github.com/loopback4/loopback-component-crud)**: [loopback4/ra-data-lb4](https://github.com/loopback4/ra-data-lb4) -* **[Mixer](https://github.com/ckoliber/ra-data-mixer)**: [ckoliber/ra-data-mixer](https://github.com/ckoliber/ra-data-mixer) -* **[Moleculer Microservices](https://github.com/RancaguaInnova/moleculer-data-provider)**: [RancaguaInnova/moleculer-data-provider](https://github.com/RancaguaInnova/moleculer-data-provider) -* **[NestJS CRUD](https://github.com/nestjsx/crud)**: [rayman1104/ra-data-nestjsx-crud](https://github.com/rayman1104/ra-data-nestjsx-crud) -* **[OData](https://www.odata.org/)**: [Groopit/ra-data-odata-server](https://github.com/Groopit/ra-data-odata-server) -* **[OpenCRUD](https://www.opencrud.org/)**: [weakky/ra-data-opencrud](https://github.com/Weakky/ra-data-opencrud) -* **[Parse](https://parseplatform.org/)**: [almahdi/ra-data-parse](https://github.com/almahdi/ra-data-parse) -* **[PostGraphile](https://www.graphile.org/postgraphile/)**: [bowlingx/ra-postgraphile](https://github.com/BowlingX/ra-postgraphile) -* **[PostgREST](https://postgrest.org/)**: [raphiniert-com/ra-data-postgrest](https://github.com/raphiniert-com/ra-data-postgrest) -* **[Prisma](https://github.com/weakky/ra-data-prisma)**: [weakky/ra-data-prisma](https://github.com/weakky/ra-data-prisma) -* **[Prisma Version 2](https://www.prisma.io/)**: [panter/ra-data-prisma](https://github.com/panter/ra-data-prisma) -* **[ProcessMaker3](https://www.processmaker.com/)**: [ckoliber/ra-data-processmaker3](https://github.com/ckoliber/ra-data-processmaker3) -* **[REST-HAPI](https://github.com/JKHeadley/rest-hapi)**: [ra-data-rest-hapi](https://github.com/mkg20001/ra-data-rest-hapi) -* **[Sails.js](https://sailsjs.com/)**: [mpampin/ra-data-json-sails](https://github.com/mpampin/ra-data-json-sails) -* **[Spring Boot](https://spring.io/projects/spring-boot)**: [vishpat/ra-data-springboot-rest](https://github.com/vishpat/ra-data-springboot-rest) -* **[Strapi](https://strapi.io/)**: [nazirov91/ra-strapi-rest](https://github.com/nazirov91/ra-strapi-rest) -- **[Supabase](https://supabase.io/)**: [marmelab/ra-supabase](https://github.com/marmelab/ra-supabase) +- **[AWS Amplify](https://docs.amplify.aws)**: [MrHertal/react-admin-amplify](https://github.com/MrHertal/react-admin-amplify) +- **[Configurable Identity Property REST Client](https://github.com/zachrybaker/ra-data-rest-client)**: [zachrybaker/ra-data-rest-client](https://github.com/zachrybaker/ra-data-rest-client) +- **[coreBOS](https://corebos.com/)**: [React-Admin coreBOS Integration](https://github.com/coreBOS/reactadminportal) +- **[Django Rest Framework](https://www.django-rest-framework.org/)**: [bmihelac/ra-data-django-rest-framework](https://github.com/bmihelac/ra-data-django-rest-framework) +- **[Eve](https://docs.python-eve.org/en/stable/)**: [smeng9/ra-data-eve](https://github.com/smeng9/ra-data-eve) +- **[Express & Sequelize](https://github.com/lalalilo/express-sequelize-crud)**: [express-sequelize-crud](https://github.com/lalalilo/express-sequelize-crud) +- **[Feathersjs](https://www.feathersjs.com/)**: [josx/ra-data-feathers](https://github.com/josx/ra-data-feathers) +- **[Firebase Firestore](https://firebase.google.com/docs/firestore)**: [benwinding/react-admin-firebase](https://github.com/benwinding/react-admin-firebase). +- **[Firebase Realtime Database](https://firebase.google.com/docs/database)**: [aymendhaya/ra-data-firebase-client](https://github.com/aymendhaya/ra-data-firebase-client). +- **[Google Sheets](https://www.google.com/sheets/about/)**: [marmelab/ra-data-google-sheets](https://github.com/marmelab/ra-data-google-sheets) +- **[GraphQL](https://graphql.org/)**: [marmelab/ra-data-graphql](https://github.com/marmelab/react-admin/tree/master/packages/ra-data-graphql) (uses [Apollo](https://www.apollodata.com/)) +- **[HAL](http://stateless.co/hal_specification.html)**: [b-social/ra-data-hal](https://github.com/b-social/ra-data-hal) +- **[Hasura](https://github.com/hasura/graphql-engine)**: [hasura/ra-data-hasura](https://github.com/hasura/ra-data-hasura), auto generates valid GraphQL queries based on the properties exposed by the Hasura API. +- **[Hydra](https://www.hydra-cg.com/) / [JSON-LD](https://json-ld.org/)**: [api-platform/admin/hydra](https://github.com/api-platform/admin/blob/master/src/hydra/dataProvider.js) +- **[IndexedDB](https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API)**: [tykoth/ra-data-dexie](https://github.com/tykoth/ra-data-dexie) +- **[JSON API](https://jsonapi.org/)**: [henvo/ra-jsonapi-client](https://github.com/henvo/ra-jsonapi-client) +- **[JSON HAL](https://tools.ietf.org/html/draft-kelly-json-hal-08)**: [ra-data-json-hal](https://www.npmjs.com/package/ra-data-json-hal) +- **[JSON server](https://github.com/typicode/json-server)**: [marmelab/ra-data-json-server](https://github.com/marmelab/react-admin/tree/master/packages/ra-data-json-server). +- **[LocalStorage](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage)**: [marmelab/ra-data-localstorage](https://github.com/marmelab/react-admin/tree/master/packages/ra-data-localstorage) +- **[Loopback3](https://loopback.io/lb3)**: [darthwesker/react-admin-loopback](https://github.com/darthwesker/react-admin-loopback) +- **[Loopback4](https://loopback.io/)**: [elmaistrenko/react-admin-lb4](https://github.com/elmaistrenko/react-admin-lb4) +- **[Loopback4 CRUD](https://github.com/loopback4/loopback-component-crud)**: [loopback4/ra-data-lb4](https://github.com/loopback4/ra-data-lb4) +- **[Mixer](https://github.com/ckoliber/ra-data-mixer)**: [ckoliber/ra-data-mixer](https://github.com/ckoliber/ra-data-mixer) +- **[Moleculer Microservices](https://github.com/RancaguaInnova/moleculer-data-provider)**: [RancaguaInnova/moleculer-data-provider](https://github.com/RancaguaInnova/moleculer-data-provider) +- **[NestJS CRUD](https://github.com/nestjsx/crud)**: [rayman1104/ra-data-nestjsx-crud](https://github.com/rayman1104/ra-data-nestjsx-crud) +- **[OData](https://www.odata.org/)**: [Groopit/ra-data-odata-server](https://github.com/Groopit/ra-data-odata-server) +- **[OpenCRUD](https://www.opencrud.org/)**: [weakky/ra-data-opencrud](https://github.com/Weakky/ra-data-opencrud) +- **[Parse](https://parseplatform.org/)**: [almahdi/ra-data-parse](https://github.com/almahdi/ra-data-parse) +- **[PostGraphile](https://www.graphile.org/postgraphile/)**: [bowlingx/ra-postgraphile](https://github.com/BowlingX/ra-postgraphile) +- **[PostgREST](https://postgrest.org/)**: [raphiniert-com/ra-data-postgrest](https://github.com/raphiniert-com/ra-data-postgrest) +- **[Prisma](https://github.com/weakky/ra-data-prisma)**: [weakky/ra-data-prisma](https://github.com/weakky/ra-data-prisma) +- **[Prisma Version 2](https://www.prisma.io/)**: [panter/ra-data-prisma](https://github.com/panter/ra-data-prisma) +- **[ProcessMaker3](https://www.processmaker.com/)**: [ckoliber/ra-data-processmaker3](https://github.com/ckoliber/ra-data-processmaker3) +- **[REST-HAPI](https://github.com/JKHeadley/rest-hapi)**: [ra-data-rest-hapi](https://github.com/mkg20001/ra-data-rest-hapi) +- **[Sails.js](https://sailsjs.com/)**: [mpampin/ra-data-json-sails](https://github.com/mpampin/ra-data-json-sails) +- **[Spring Boot](https://spring.io/projects/spring-boot)**: [vishpat/ra-data-springboot-rest](https://github.com/vishpat/ra-data-springboot-rest) +- **[Strapi](https://strapi.io/)**: [nazirov91/ra-strapi-rest](https://github.com/nazirov91/ra-strapi-rest) + +* **[Supabase](https://supabase.io/)**: [marmelab/ra-supabase](https://github.com/marmelab/ra-supabase) If you've written a Data Provider for another backend, and open-sourced it, please help complete this list with your package. **Tip**: In version 1, react-admin was called [admin-on-rest](https://github.com/marmelab/admin-on-rest) (AOR), and developers shared Data Providers for even more backends. Due to breaking changes in v2, these providers are no longer working. Fortunately, Data Providers aren't complex pieces of code, and using legacy Data Provider with a recent react-admin version requires minimal changes. If you are a maintainer of one of these projects, we would warmly welcome an upgrade. -* **[DynamoDb](https://github.com/abiglobalhealth/aor-dynamodb-client)**: [abiglobalhealth/aor-dynamodb-client](https://github.com/abiglobalhealth/aor-dynamodb-client) -* **[Epilogue](https://github.com/dchester/epilogue)**: [dunghuynh/aor-epilogue-client](https://github.com/dunghuynh/aor-epilogue-client) -* **[Parse Server](https://github.com/ParsePlatform/parse-server)**: [leperone/aor-parseserver-client](https://github.com/leperone/aor-parseserver-client) -* **[Xmysql](https://github.com/o1lab/xmysql)**: [soaserele/aor-xmysql](https://github.com/soaserele/aor-xmysql) +- **[DynamoDb](https://github.com/abiglobalhealth/aor-dynamodb-client)**: [abiglobalhealth/aor-dynamodb-client](https://github.com/abiglobalhealth/aor-dynamodb-client) +- **[Epilogue](https://github.com/dchester/epilogue)**: [dunghuynh/aor-epilogue-client](https://github.com/dunghuynh/aor-epilogue-client) +- **[Parse Server](https://github.com/ParsePlatform/parse-server)**: [leperone/aor-parseserver-client](https://github.com/leperone/aor-parseserver-client) +- **[Xmysql](https://github.com/o1lab/xmysql)**: [soaserele/aor-xmysql](https://github.com/soaserele/aor-xmysql) ## Usage @@ -130,7 +126,7 @@ Then, initialize the provider with the REST backend URL, and pass the result to ```jsx // in src/App.js -import * as React from "react"; +import * as React from 'react'; import { Admin, Resource } from 'react-admin'; import simpleRestProvider from 'ra-data-simple-rest'; @@ -153,7 +149,7 @@ Here is how this Data Provider maps react-admin calls to API calls: | `getOne` | `GET http://my.api.url/posts/123` | | `getMany` | `GET http://my.api.url/posts?filter={"id":[123,456,789]}` | | `getManyReference` | `GET http://my.api.url/posts?filter={"author_id":345}` | -| `create` | `POST http://my.api.url/posts` | +| `create` | `POST http://my.api.url/posts` | | `update` | `PUT http://my.api.url/posts/123` | | `updateMany` | Multiple calls to `PUT http://my.api.url/posts/123` | | `delete` | `DELETE http://my.api.url/posts/123` | @@ -175,7 +171,7 @@ Access-Control-Expose-Headers: Content-Range The `simpleRestProvider` function accepts an HTTP client function as second argument. By default, it uses react-admin's `fetchUtils.fetchJson()` function as HTTP client. It's similar to HTML5 `fetch()`, except it handles JSON decoding and HTTP error codes automatically. -That means that if you need to add custom headers to your requests, you can just *wrap* the `fetchJson()` call inside your own function: +That means that if you need to add custom headers to your requests, you can just _wrap_ the `fetchJson()` call inside your own function: ```jsx import { fetchUtils, Admin, Resource } from 'react-admin'; @@ -188,7 +184,7 @@ const fetchJson = (url, options = {}) => { // add your own headers here options.headers.set('X-Custom-Header', 'foobar'); return fetchUtils.fetchJson(url, options); -} +}; const dataProvider = simpleRestProvider('http://path.to.my.api/', fetchJson); const App = () => ( @@ -206,7 +202,7 @@ Now all the requests to the REST API will contain the `X-Custom-Header: foobar` const fetchJson = (url, options = {}) => { options.user = { authenticated: true, - token: 'SRTRDFVESGNJYTUKTYTHRG' + token: 'SRTRDFVESGNJYTUKTYTHRG', }; return fetchUtils.fetchJson(url, options); }; @@ -217,7 +213,7 @@ Now all the requests to the REST API will contain the `Authorization: SRTRDFVESG ## Extending a Data Provider (Example of File Upload) -As Data Providers are just objects, you can extend them with custom logic for a given method, or a given resource. +As Data Providers are just objects, you can extend them with custom logic for a given method, or a given resource. For instance, the following Data Provider extends the `ra-data-simple-rest` provider, and adds image upload support for the `update('posts')` call (react-admin offers an `` component that allows image upload). @@ -237,7 +233,7 @@ const myDataProvider = { * For posts update only, convert uploaded image in base 64 and attach it to * the `picture` sent property, with `src` and `title` attributes. */ - + // Freshly dropped pictures are File objects and must be converted to base64 strings const newPictures = params.data.pictures.filter( p => p.rawFile instanceof File @@ -289,11 +285,11 @@ Using this technique, you can also combine two Data Providers for two backends i ## Writing Your Own Data Provider -APIs are so diverse that quite often, none of the available Data Providers suit you API. In such cases, you'll have to write your own Data Provider. Don't worry, it usually takes only a couple of hours. +APIs are so diverse that quite often, none of the available Data Providers suit you API. In such cases, you'll have to write your own Data Provider. Don't worry, it usually takes only a couple of hours. The methods of a Data Provider receive a request, and return a promise for a response. Both the request and the response format are standardized. -**Caution**: A Data Provider should return the same shape in `getList` and `getOne` for a given resource. This is because react-admin uses "optimistic rendering", and renders the Edit and Show view *before* calling `dataProvider.getOne()` by reusing the response from `dataProvider.getList()` if the user has displayed the List view before. If your API has different shapes for a query for a unique record and for a query for a list of records, your Data Provider should make these records consistent in shape before returning them to react-admin. +**Caution**: A Data Provider should return the same shape in `getList` and `getOne` for a given resource. This is because react-admin uses "optimistic rendering", and renders the Edit and Show view _before_ calling `dataProvider.getOne()` by reusing the response from `dataProvider.getList()` if the user has displayed the List view before. If your API has different shapes for a query for a unique record and for a query for a list of records, your Data Provider should make these records consistent in shape before returning them to react-admin. For instance, the following Data Provider returns more details in `getOne` than in `getList`: @@ -302,25 +298,25 @@ const { data } = await dataProvider.getList('posts', { pagination: { page: 1, perPage: 5 }, sort: { field: 'title', order: 'ASC' }, filter: { author_id: 12 }, -}) +}); // [ // { id: 123, title: "hello, world", author_id: 12 }, // { id: 125, title: "howdy partner", author_id: 12 }, // ], -const { data } = dataProvider.getOne('posts', { id: 123 }) +const { data } = dataProvider.getOne('posts', { id: 123 }); // { // data: { id: 123, title: "hello, world", author_id: 12, body: 'Lorem Ipsum Sic Dolor Amet' } // } ``` -This will cause the Edit view to blink on load. If you have this problem, modify your Data Provider to return the same shape for all methods. +This will cause the Edit view to blink on load. If you have this problem, modify your Data Provider to return the same shape for all methods. ## Request Format -Data queries require a *method* (e.g. `getOne`), a *resource* (e.g. 'posts') and a set of *parameters*. +Data queries require a _method_ (e.g. `getOne`), a _resource_ (e.g. 'posts') and a set of _parameters_. -**Tip**: In comparison, HTTP requests require a *verb* (e.g. 'GET'), an *url* (e.g. 'http://myapi.com/posts'), a list of *headers* (like `Content-Type`) and a *body*. +**Tip**: In comparison, HTTP requests require a _verb_ (e.g. 'GET'), an _url_ (e.g. 'http://myapi.com/posts'), a list of _headers_ (like `Content-Type`) and a _body_. Standard methods are: @@ -349,21 +345,21 @@ dataProvider.getMany('posts', { ids: [123, 124, 125] }); dataProvider.getManyReference('comments', { target: 'post_id', id: 123, - sort: { field: 'created_at', order: 'DESC' } + sort: { field: 'created_at', order: 'DESC' }, }); dataProvider.update('posts', { id: 123, - data: { title: "hello, world!" }, - previousData: { title: "previous title" } + data: { title: 'hello, world!' }, + previousData: { title: 'previous title' }, }); dataProvider.updateMany('posts', { ids: [123, 234], data: { views: 0 }, }); -dataProvider.create('posts', { data: { title: "hello, world" } }); +dataProvider.create('posts', { data: { title: 'hello, world' } }); dataProvider.delete('posts', { id: 123, - previousData: { title: "hello, world" } + previousData: { title: 'hello, world' }, }); dataProvider.deleteMany('posts', { ids: [123, 234] }); ``` @@ -391,12 +387,13 @@ A `{Record}` is an object literal with at least an `id` property, e.g. `{ id: 12 Building up on the previous example, here are example responses matching the format expected by react-admin: ```js -dataProvider.getList('posts', { - pagination: { page: 1, perPage: 5 }, - sort: { field: 'title', order: 'ASC' }, - filter: { author_id: 12 }, -}) -.then(response => console.log(response)); +dataProvider + .getList('posts', { + pagination: { page: 1, perPage: 5 }, + sort: { field: 'title', order: 'ASC' }, + filter: { author_id: 12 }, + }) + .then(response => console.log(response)); // { // data: [ // { id: 126, title: "allo?", author_id: 12 }, @@ -408,14 +405,16 @@ dataProvider.getList('posts', { // total: 27 // } -dataProvider.getOne('posts', { id: 123 }) -.then(response => console.log(response)); +dataProvider + .getOne('posts', { id: 123 }) + .then(response => console.log(response)); // { // data: { id: 123, title: "hello, world" } // } -dataProvider.getMany('posts', { ids: [123, 124, 125] }) -.then(response => console.log(response)); +dataProvider + .getMany('posts', { ids: [123, 124, 125] }) + .then(response => console.log(response)); // { // data: [ // { id: 123, title: "hello, world" }, @@ -424,12 +423,13 @@ dataProvider.getMany('posts', { ids: [123, 124, 125] }) // ] // } -dataProvider.getManyReference('comments', { - target: 'post_id', - id: 123, - sort: { field: 'created_at', order: 'DESC' } -}) -.then(response => console.log(response)); +dataProvider + .getManyReference('comments', { + target: 'post_id', + id: 123, + sort: { field: 'created_at', order: 'DESC' }, + }) + .then(response => console.log(response)); // { // data: [ @@ -439,42 +439,47 @@ dataProvider.getManyReference('comments', { // total: 2, // } -dataProvider.create('posts', { data: { title: "hello, world" } }) -.then(response => console.log(response)); +dataProvider + .create('posts', { data: { title: 'hello, world' } }) + .then(response => console.log(response)); // { // data: { id: 450, title: "hello, world" } // } -dataProvider.update('posts', { - id: 123, - data: { title: "hello, world!" }, - previousData: { title: "previous title" } -}) -.then(response => console.log(response)); +dataProvider + .update('posts', { + id: 123, + data: { title: 'hello, world!' }, + previousData: { title: 'previous title' }, + }) + .then(response => console.log(response)); // { // data: { id: 123, title: "hello, world!" } // } -dataProvider.updateMany('posts', { - ids: [123, 234], - data: { views: 0 }, -}) -.then(response => console.log(response)); +dataProvider + .updateMany('posts', { + ids: [123, 234], + data: { views: 0 }, + }) + .then(response => console.log(response)); // { // data: [123, 234] // } -dataProvider.delete('posts', { - id: 123, - previousData: { title: "hello, world!" } -}) -.then(response => console.log(response)); +dataProvider + .delete('posts', { + id: 123, + previousData: { title: 'hello, world!' }, + }) + .then(response => console.log(response)); // { // data: { id: 123, title: "hello, world" } // } -dataProvider.deleteMany('posts', { ids: [123, 234] }) -.then(response => console.log(response)); +dataProvider + .deleteMany('posts', { ids: [123, 234] }) + .then(response => console.log(response)); // { // data: [123, 234] // } @@ -533,7 +538,6 @@ export default { Let's say that you want to map the react-admin requests to a REST backend exposing the following API: - ### getList ``` @@ -708,7 +712,7 @@ export default { updateMany: (resource, params) => { const query = { - filter: JSON.stringify({ id: params.ids}), + filter: JSON.stringify({ id: params.ids }), }; return httpClient(`${apiUrl}/${resource}?${stringify(query)}`, { method: 'PUT', @@ -731,7 +735,7 @@ export default { deleteMany: (resource, params) => { const query = { - filter: JSON.stringify({ id: params.ids}), + filter: JSON.stringify({ id: params.ids }), }; return httpClient(`${apiUrl}/${resource}?${stringify(query)}`, { method: 'DELETE', @@ -745,19 +749,19 @@ export default { React-admin stores the Data Provider passed to `` in a React context, so you can access it from anywhere in your code. To facilitate usage, react-admin provides many data provider hooks: -* `useDataProvider` -* `useQuery` -* `useQueryWithStore` -* `useMutation` -* `useGetList` -* `useGetOne` -* `useGetMany` -* `useGetManyReference` -* `useCreate` -* `useUpdate` -* `useUpdateMany` -* `useDelete` -* `useDeleteMany` +- `useDataProvider` +- `useQuery` +- `useQueryWithStore` +- `useMutation` +- `useGetList` +- `useGetOne` +- `useGetMany` +- `useGetManyReference` +- `useCreate` +- `useUpdate` +- `useUpdateMany` +- `useDelete` +- `useDeleteMany` Here is a glimpse of the `useGetOne` hook usage: @@ -766,8 +770,12 @@ import { useGetOne } from 'react-admin'; const UserProfile = ({ record }) => { const { data, loading, error } = useGetOne('users', record.id); - if (loading) { return ; } - if (error) { return

ERROR

; } + if (loading) { + return ; + } + if (error) { + return

ERROR

; + } return
User {data.username}
; }; ``` @@ -776,9 +784,9 @@ You will find complete usage documentation for the data provider hooks in the [Q ## Real-Time Updates And Locks -Teams where several people work in parallel on a common task need to allow live updates, real-time notifications, and prevent data loss when two editors work on the same resource concurrently. +Teams where several people work in parallel on a common task need to allow live updates, real-time notifications, and prevent data loss when two editors work on the same resource concurrently. -[`ra-realtime`](https://marmelab.com/ra-enterprise/modules/ra-realtime) (an [Enterprise Edition ](https://marmelab.com/ra-enterprise) module) provides hooks and UI components to lock records, and update views when the underlying data changes. It's based on the Publish / Subscribe (PubSub) pattern, and requires a backend supporting this pattern (like GraphQL, Mercure). +[`ra-realtime`](https://marmelab.com/ra-enterprise/modules/ra-realtime) (an [Enterprise Edition ](https://marmelab.com/ra-enterprise) module) provides hooks and UI components to lock records, and update views when the underlying data changes. It's based on the Publish / Subscribe (PubSub) pattern, and requires a backend supporting this pattern (like GraphQL, Mercure). For instance, here is how to enable live updates on a List view: @@ -806,4 +814,3 @@ const PostList = props => ( ``` Check [the `ra-realtime` documentation](https://marmelab.com/ra-enterprise/modules/ra-realtime) for more details. - diff --git a/docs/Inputs.md b/docs/Inputs.md index 2bcdb302e12..796a446fcfa 100644 --- a/docs/Inputs.md +++ b/docs/Inputs.md @@ -1,6 +1,6 @@ --- layout: default -title: "Input Components" +title: 'Input Components' --- # Input Components @@ -9,17 +9,33 @@ An `Input` component displays an input, or a dropdown list, a list of radio butt ```jsx // in src/posts.js -import * as React from "react"; -import { Edit, SimpleForm, ReferenceInput, SelectInput, TextInput, required } from 'react-admin'; +import * as React from 'react'; +import { + Edit, + SimpleForm, + ReferenceInput, + SelectInput, + TextInput, + required, +} from 'react-admin'; -export const PostEdit = (props) => ( +export const PostEdit = props => ( } {...props}> - + - + @@ -30,15 +46,15 @@ export const PostEdit = (props) => ( All input components accept the following props: -| Prop | Required | Type | Default | Description | -| --------------- | -------- | ------------------------- | ------- | ---------------------------------------------------------------------------------------------------------------------------- | -| `source` | Required | `string` | - | Name of the entity property to use for the input value | +| Prop | Required | Type | Default | Description | +| --------------- | -------- | ------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `source` | Required | `string` | - | Name of the entity property to use for the input value | | `label` | Optional | `string` | - | Input label. In i18n apps, the label is passed to the `translate` function. Defaults to the humanized `source` when omitted. Set `label={false}` to hide the label. | -| `validate` | Optional | `Function` | `array` | - | Validation rules for the current property. See the [Validation Documentation](./CreateEdit.md#validation) for details. | -| `helperText` | Optional | `string` | - | Text to be displayed under the input | -| `fullWidth` | Optional | `boolean` | `false` | If `true`, the input will expand to fill the form width | -| `className` | Optional | `string` | - | Class name (usually generated by JSS) to customize the look and feel of the field element itself | -| `formClassName` | Optional | `string` | - | Class name to be applied to the container of the input (e.g. the `
` forming each row in ``) | +| `validate` | Optional | `Function` | `array` | - | Validation rules for the current property. See the [Validation Documentation](./CreateEdit.md#validation) for details. | +| `helperText` | Optional | `string` | - | Text to be displayed under the input | +| `fullWidth` | Optional | `boolean` | `false` | If `true`, the input will expand to fill the form width | +| `className` | Optional | `string` | - | Class name (usually generated by JSS) to customize the look and feel of the field element itself | +| `formClassName` | Optional | `string` | - | Class name to be applied to the container of the input (e.g. the `
` forming each row in ``) | ```jsx @@ -86,7 +102,7 @@ Then you can display a text input to edit the author first name as follows: ```jsx import { BooleanInput } from 'react-admin'; - +; ``` ![BooleanInput](./img/boolean-input.png) @@ -96,6 +112,7 @@ This input does not handle `null` values. You would need the `, }} -/> +/>; ``` + {% endraw %} ![CustomBooleanInputCheckIcon](./img/custom-switch-icon.png) @@ -118,7 +136,7 @@ Refer to [Material UI Switch documentation](https://material-ui.com/api/switch) ```jsx import { NullableBooleanInput } from 'react-admin'; - +; ``` ![NullableBooleanInput](./img/nullable-boolean-input.gif) @@ -134,7 +152,7 @@ englishMessages.ra.boolean.false = 'False label'; englishMessages.ra.boolean.true = 'True label'; const i18nProvider = polyglotI18nProvider(() => englishMessages, 'en'); - +; ``` Additionally, individual instances of `NullableBooleanInput` may be customized by setting the `nullLabel`, `falseLabel` and `trueLabel` properties. Values specified for those properties will be translated by react-admin. @@ -148,7 +166,7 @@ import { NullableBooleanInput } from 'react-admin'; nullLabel="Either" falseLabel="No" trueLabel="Yes" -/> +/>; ``` ![NullableBooleanInput](./img/nullable-boolean-input-null-label.png) @@ -157,15 +175,15 @@ import { NullableBooleanInput } from 'react-admin'; #### CSS API -| Rule name | Description | -| ---------- | ------------------------------------------------------------- | -| `input` | Applied to the underlying Material UI's `TextField` component | +| Rule name | Description | +| --------- | ------------------------------------------------------------- | +| `input` | Applied to the underlying Material UI's `TextField` component | To override the style of all instances of `` using the [material-ui style overrides](https://material-ui.com/customization/globals/#css), use the `RaNullableBooleanInput` key. ### `` -Ideal for editing dates, `` renders an HTML `` element, that most browsers display as a standard [Date Picker](https://material-ui.com/components/pickers/#date-pickers). +Ideal for editing dates, `` renders an HTML `` element, that most browsers display as a standard [Date Picker](https://material-ui.com/components/pickers/#date-pickers). ![DateInput](./img/date-input.gif) @@ -174,7 +192,7 @@ The appearance of `` depends on the browser, and falls back to a text ```jsx import { DateInput } from 'react-admin'; - +; ``` `` also accepts the [common input props](./Inputs.md#common-input-props). @@ -192,7 +210,7 @@ An input for editing dates with time. `` renders a standard brows ```jsx import { DateTimeInput } from 'react-admin'; - +; ``` `` also accepts the [common input props](./Inputs.md#common-input-props). @@ -207,17 +225,17 @@ import { DateTimeInput } from 'react-admin'; #### Properties -| Prop | Required | Type | Default | Description | -| --------------- | -------- | --------------------------- | -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `accept` | Optional | `string | string[]` | - | Accepted file type(s), e. g. 'image/*,.pdf'. If left empty, all file types are accepted. Equivalent of the `accept` attribute of an ``. See [MDN input docs](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file#accept) for syntax and examples. | -| `children` | Optional | `ReactNode` | - | Element used to display the preview of an image (cloned several times if the select accepts multiple files). | -| `minSize` | Optional | `number` | 0 | Minimum image size (in bytes), e.g. 5000 for 5KB | -| `maxSize` | Optional | `number` | `Infinity` | Maximum image size (in bytes), e.g. 5000000 for 5MB | -| `multiple` | Optional | `boolean` | `false` | Set to true if the input should accept a list of images, false if it should only accept one image | -| `labelSingle` | Optional | `string` | 'ra.input.image. upload_single' | Invite displayed in the drop zone if the input accepts one image | -| `labelMultiple` | Optional | `string` | 'ra.input.file. upload_multiple' | Invite displayed in the drop zone if the input accepts several images | -| `placeholder` | Optional | `string` | `ReactNode` | - | Invite displayed in the drop zone, overrides `labelSingle` and `labelMultiple` | -| `options` | Optional | `Object` | `{}` | Additional options passed to react-dropzone's `useDropzone()` hook. See [the react-dropzone source](https://github.com/react-dropzone/react-dropzone/blob/master/src/index.js) for details . | +| Prop | Required | Type | Default | Description | +| --------------- | -------- | --------------------------- | -------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `accept` | Optional | `string | string[]` | - | Accepted file type(s), e. g. 'image/\*,.pdf'. If left empty, all file types are accepted. Equivalent of the `accept` attribute of an ``. See [MDN input docs](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file#accept) for syntax and examples. | +| `children` | Optional | `ReactNode` | - | Element used to display the preview of an image (cloned several times if the select accepts multiple files). | +| `minSize` | Optional | `number` | 0 | Minimum image size (in bytes), e.g. 5000 for 5KB | +| `maxSize` | Optional | `number` | `Infinity` | Maximum image size (in bytes), e.g. 5000000 for 5MB | +| `multiple` | Optional | `boolean` | `false` | Set to true if the input should accept a list of images, false if it should only accept one image | +| `labelSingle` | Optional | `string` | 'ra.input.image. upload_single' | Invite displayed in the drop zone if the input accepts one image | +| `labelMultiple` | Optional | `string` | 'ra.input.file. upload_multiple' | Invite displayed in the drop zone if the input accepts several images | +| `placeholder` | Optional | `string` | `ReactNode` | - | Invite displayed in the drop zone, overrides `labelSingle` and `labelMultiple` | +| `options` | Optional | `Object` | `{}` | Additional options passed to react-dropzone's `useDropzone()` hook. See [the react-dropzone source](https://github.com/react-dropzone/react-dropzone/blob/master/src/index.js) for details . | `` also accepts the [common input props](./Inputs.md#common-input-props). @@ -242,7 +260,12 @@ The `ImageInput` component accepts an `options` prop, allowing to set the [react If the default Dropzone label doesn't fit with your need, you can pass a `placeholder` prop to overwrite it. The value can be anything React can render (`PropTypes.node`): ```jsx -Drop your file here

}> +Drop your file here

} +>
``` @@ -251,12 +274,12 @@ Note that the image upload returns a [File](https://developer.mozilla.org/en/doc #### CSS API -| Rule name | Description | -| --------------- | ---------------------------------------------------- | -| `root` | Styles pass to the underlying `FileInput` component | -| `dropZone` | Styles pass to the underlying `FileInput` component | -| `preview` | Styles pass to the underlying `FileInput` component | -| `removeButton` | Styles pass to the underlying `FileInput` component | +| Rule name | Description | +| -------------- | --------------------------------------------------- | +| `root` | Styles pass to the underlying `FileInput` component | +| `dropZone` | Styles pass to the underlying `FileInput` component | +| `preview` | Styles pass to the underlying `FileInput` component | +| `removeButton` | Styles pass to the underlying `FileInput` component | To override the style of all instances of `` using the [material-ui style overrides](https://material-ui.com/customization/globals/#css), use the `RaImageInput` key. @@ -268,17 +291,17 @@ To override the style of all instances of `` using the [material-ui #### Properties -| Prop | Required | Type | Default | Description | -| --------------- | -------- | -------------------- | ------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `accept` | Optional | `string | string[]` | - | Accepted file type(s), e. g. 'application/json,video/*' or 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'. If left empty, all file types are accepted. Equivalent of the `accept` attribute of an ``. See [MDN input docs](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file#accept) for syntax and examples. | -| `children` | Optional | `ReactNode` | - | Element used to display the preview of a file (cloned several times if the select accepts multiple files). | -| `minSize` | Optional | `number` | 0 | Minimum file size (in bytes), e.g. 5000 for 5KB | -| `maxSize` | Optional | `number` | `Infinity` | Maximum file size (in bytes), e.g. 5000000 for 5MB | -| `multiple` | Optional | `boolean` | `false` | Set to true if the input should accept a list of files, false if it should only accept one file | -| `labelSingle` | Optional | `string` | 'ra.input.file. upload_single' | Invite displayed in the drop zone if the input accepts one file | -| `labelMultiple` | Optional | `string` | 'ra.input.file. upload_several' | Invite displayed in the drop zone if the input accepts several files | -| `placeholder` | Optional | `ReactNode` | - | Invite displayed in the drop zone, overrides `labelSingle` and `labelMultiple` | -| `options` | Optional | `Object` | `{}` | Additional options passed to react-dropzone's `useDropzone()` hook. See [the react-dropzone source](https://github.com/react-dropzone/react-dropzone/blob/master/src/index.js) for details . | +| Prop | Required | Type | Default | Description | +| --------------- | -------- | ----------- | ------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `accept` | Optional | `string | string[]` | - | Accepted file type(s), e. g. 'application/json,video/\*' or 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'. If left empty, all file types are accepted. Equivalent of the `accept` attribute of an ``. See [MDN input docs](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file#accept) for syntax and examples. | +| `children` | Optional | `ReactNode` | - | Element used to display the preview of a file (cloned several times if the select accepts multiple files). | +| `minSize` | Optional | `number` | 0 | Minimum file size (in bytes), e.g. 5000 for 5KB | +| `maxSize` | Optional | `number` | `Infinity` | Maximum file size (in bytes), e.g. 5000000 for 5MB | +| `multiple` | Optional | `boolean` | `false` | Set to true if the input should accept a list of files, false if it should only accept one file | +| `labelSingle` | Optional | `string` | 'ra.input.file. upload_single' | Invite displayed in the drop zone if the input accepts one file | +| `labelMultiple` | Optional | `string` | 'ra.input.file. upload_several' | Invite displayed in the drop zone if the input accepts several files | +| `placeholder` | Optional | `ReactNode` | - | Invite displayed in the drop zone, overrides `labelSingle` and `labelMultiple` | +| `options` | Optional | `Object` | `{}` | Additional options passed to react-dropzone's `useDropzone()` hook. See [the react-dropzone source](https://github.com/react-dropzone/react-dropzone/blob/master/src/index.js) for details . | `` also accepts the [common input props](./Inputs.md#common-input-props). @@ -298,12 +321,17 @@ Writing a custom preview component is quite straightforward: it's a standard [fi When receiving **new** files, `FileInput` will add a `rawFile` property to the object passed as the `record` prop of children. This `rawFile` is the [File](https://developer.mozilla.org/en-US/docs/Web/API/File) instance of the newly added file. This can be useful to display information about size or MIME type inside a custom field. -The `FileInput` component accepts an `options` prop into which you can pass all the [react-dropzone properties](https://react-dropzone.netlify.com/#proptypes). +The `FileInput` component accepts an `options` prop into which you can pass all the [react-dropzone properties](https://react-dropzone.netlify.com/#proptypes). If the default Dropzone label doesn't fit with your need, you can pass a `placeholder` prop to overwrite it. The value can be anything React can render (`PropTypes.node`): ```jsx -Drop your file here

}> +Drop your file here

} +>
``` @@ -312,12 +340,12 @@ Note that the file upload returns a [File](https://developer.mozilla.org/en/docs #### CSS API -| Rule name | Description | -| --------------- | --------------------------------------------------------------------------------- | -| `root` | Applied to the underlying `Labeled` component | -| `dropZone` | Applied to the main container of the component | -| `preview` | Applied to each children | -| `removeButton` | Applied to each of the Material UI's `IconButton` component used as remove button | +| Rule name | Description | +| -------------- | --------------------------------------------------------------------------------- | +| `root` | Applied to the underlying `Labeled` component | +| `dropZone` | Applied to the main container of the component | +| `preview` | Applied to each children | +| `removeButton` | Applied to each of the Material UI's `IconButton` component used as remove button | To override the style of all instances of `` using the [material-ui style overrides](https://material-ui.com/customization/globals/#css), use the `RaFileInput` key. @@ -345,7 +373,7 @@ Check [the `ra-markdown` documentation](https://marmelab.com/ra-enterprise/modul ### `` -`` translates to an HTML ``. +`` translates to an HTML ``. ![NumberInput](./img/number-input.gif) @@ -354,7 +382,7 @@ It is necessary for numeric values because of a [known React bug](https://github ```jsx import { NumberInput } from 'react-admin'; - +; ``` #### Properties @@ -381,7 +409,7 @@ You can customize the `step` props (which defaults to "any"). For instance, to r ```jsx import { PasswordInput } from 'react-admin'; - +; ``` ![Password Input](./img/password-input.png) @@ -390,7 +418,7 @@ It is possible to change the default behavior and display the value by default v ```jsx import { PasswordInput } from 'react-admin'; - +; ``` ![Password Input (visible)](./img/password-input-visible.png) @@ -398,9 +426,14 @@ import { PasswordInput } from 'react-admin'; **Tip**: It is possible to set the [`autocomplete` attribute](https://developer.mozilla.org/fr/docs/Web/HTML/Attributs/autocomplete) by injecting an input props: {% raw %} + ```jsx - + ``` + {% endraw %} ### `` @@ -421,50 +454,57 @@ Then use it as a normal input component: ```jsx import RichTextInput from 'ra-input-rich-text'; - +; ``` You can customize the rich text editor toolbar using the `toolbar` attribute, as described on the [Quill official toolbar documentation](https://quilljs.com/docs/modules/toolbar/). ```jsx - + ``` If you need to add Quill `modules` or `themes`, you can do so by passing them in the `options` prop. {% raw %} + ```jsx ``` + {% endraw %} If you need more customization, you can access the quill object through the `configureQuill` callback that will be called just after its initialization. ```jsx -const configureQuill = quill => quill.getModule('toolbar').addHandler('bold', function (value) { - this.quill.format('bold', value) -}); +const configureQuill = quill => + quill.getModule('toolbar').addHandler('bold', function (value) { + this.quill.format('bold', value); + }); // ... - +; ``` `` also accepts the [common input props](./Inputs.md#common-input-props). -**Tip**: When used inside a material-ui `` (e.g in the default `` view), `` displays link tooltip as cut off when the user wants to add a hyperlink to a word located on the left side of the input. This is due to an incompatibility between material-ui's `` component and Quill's positioning algorithm for the link tooltip. +**Tip**: When used inside a material-ui `` (e.g in the default `` view), `` displays link tooltip as cut off when the user wants to add a hyperlink to a word located on the left side of the input. This is due to an incompatibility between material-ui's `` component and Quill's positioning algorithm for the link tooltip. To fix this problem, you should override the default card style, as follows: @@ -492,16 +532,16 @@ import { Edit, SimpleForm, TextInput } from 'react-admin'; ```jsx import { TextInput } from 'react-admin'; - +; ``` #### Properties -| Prop | Required | Type | Default | Description | -| ------------ | -------- | --------- | ------- | -------------------------------------------------------------------- | +| Prop | Required | Type | Default | Description | +| ------------ | -------- | --------- | ------- | ------------------------------------------------------------------------ | | `multiline` | Optional | `boolean` | `false` | If `true`, the input height expands as the text wraps over several lines | -| `resettable` | Optional | `boolean` | `false` | If `true`, display a button to reset the changes in this input value | -| `type` | Optional | `string` | `text` | Type attribute passed to the `` element | +| `resettable` | Optional | `boolean` | `false` | If `true`, display a button to reset the changes in this input value | +| `type` | Optional | `string` | `text` | Type attribute passed to the `` element | `` also accepts the [common input props](./Inputs.md#common-input-props). @@ -524,7 +564,7 @@ You can make the `` component resettable using the `resettable` prop. ```jsx import { TextInput } from 'react-admin'; - +; ``` ![resettable TextInput](./img/resettable-text-input.gif) @@ -545,34 +585,37 @@ Set the `choices` attribute to determine the options list (with `id`, `name` tup ```jsx import { AutocompleteInput } from 'react-admin'; - +; ``` #### Properties -| Prop | Required | Type | Default | Description | -| ------------------------- | -------- | -------------- | ------------ | ------------------------------------ | -| `allowEmpty` | Optional | `boolean` | `false` | If `false` and the `searchText` typed did not match any suggestion, the `searchText` will revert to the current value when the field is blurred. If `true` and the `searchText` is set to `''` then the field will set the input value to `null`. | -| `clearAlwaysVisible` | Optional | `boolean` | `false` | When `resettable` is true, set this prop to `true` to have the Reset button visible even when the field is empty | -| `choices` | Required | `Object[]` | `-` | List of items to autosuggest | -| `create` | Optional | `Element` | `-` | A React Element to render when users want to create a new choice | -| `createLabel` | Optional | `string` | `ra.action.create` | The label for the menu item allowing users to create a new choice. Used when the filter is empty | -| `createItemLabel` | Optional | `string` | `ra.action.create_item` | The label for the menu item allowing users to create a new choice. Used when the filter is not empty | -| `emptyValue` | Optional | `any` | `''` | The value to use for the empty element | -| `emptyText` | Optional | `string` | `''` | The text to use for the empty element | -| `matchSuggestion` | Optional | `Function` | `-` | Required if `optionText` is a React element. Function returning a boolean indicating whether a choice matches the filter. `(filter, choice) => boolean` | -| `onCreate` | Optional | `Function` | `-` | A function called with the current filter value when users choose to create a new choice. | -| `optionText` | Optional | `string` | `Function` | `Component` | `name` | Field name of record to display in the suggestion item or function which accepts the correct record as argument (`(record)=> {string}`) | -| `optionValue` | Optional | `string` | `id` | Field name of record containing the value to use as input value | -| `inputText` | Optional | `Function` | `-` | If `optionText` is a custom Component, this function is needed to determine the text displayed for the current selection. | -| `resettable` | Optional | `boolean` | `false` | Display a button to reset the text filter. Useful when using `` inside the list filters | -| `setFilter` | Optional | `Function` | `null` | A callback to inform the `searchText` has changed and new `choices` can be retrieved based on this `searchText`. Signature `searchText => void`. This function is automatically setup when using `ReferenceInput`. | -| `shouldRenderSuggestions` | Optional | `Function` | `() => true` | A function that returns a `boolean` to determine whether or not suggestions are rendered. Use this when working with large collections of data to improve performance and user experience. This function is passed into the underlying react-autosuggest component. Ex.`(value) => value.trim() > 2` | -| `suggestionLimit` | Optional | `number` | `null` | Limits the numbers of suggestions that are shown in the dropdown list | +| Prop | Required | Type | Default | Description | +| ------------------------- | -------- | --------------------------------------------- | ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `allowEmpty` | Optional | `boolean` | `false` | If `false` and the `searchText` typed did not match any suggestion, the `searchText` will revert to the current value when the field is blurred. If `true` and the `searchText` is set to `''` then the field will set the input value to `null`. | +| `clearAlwaysVisible` | Optional | `boolean` | `false` | When `resettable` is true, set this prop to `true` to have the Reset button visible even when the field is empty | +| `choices` | Required | `Object[]` | `-` | List of items to autosuggest | +| `create` | Optional | `Element` | `-` | A React Element to render when users want to create a new choice | +| `createLabel` | Optional | `string` | `ra.action.create` | The label for the menu item allowing users to create a new choice. Used when the filter is empty | +| `createItemLabel` | Optional | `string` | `ra.action.create_item` | The label for the menu item allowing users to create a new choice. Used when the filter is not empty | +| `emptyValue` | Optional | `any` | `''` | The value to use for the empty element | +| `emptyText` | Optional | `string` | `''` | The text to use for the empty element | +| `matchSuggestion` | Optional | `Function` | `-` | Required if `optionText` is a React element. Function returning a boolean indicating whether a choice matches the filter. `(filter, choice) => boolean` | +| `onCreate` | Optional | `Function` | `-` | A function called with the current filter value when users choose to create a new choice. | +| `optionText` | Optional | `string` | `Function` | `Component` | `name` | Field name of record to display in the suggestion item or function which accepts the correct record as argument (`(record)=> {string}`) | +| `optionValue` | Optional | `string` | `id` | Field name of record containing the value to use as input value | +| `inputText` | Optional | `Function` | `-` | If `optionText` is a custom Component, this function is needed to determine the text displayed for the current selection. | +| `resettable` | Optional | `boolean` | `false` | Display a button to reset the text filter. Useful when using `` inside the list filters | +| `setFilter` | Optional | `Function` | `null` | A callback to inform the `searchText` has changed and new `choices` can be retrieved based on this `searchText`. Signature `searchText => void`. This function is automatically setup when using `ReferenceInput`. | +| `shouldRenderSuggestions` | Optional | `Function` | `() => true` | A function that returns a `boolean` to determine whether or not suggestions are rendered. Use this when working with large collections of data to improve performance and user experience. This function is passed into the underlying react-autosuggest component. Ex.`(value) => value.trim() > 2` | +| `suggestionLimit` | Optional | `number` | `null` | Limits the numbers of suggestions that are shown in the dropdown list | `` also accepts the [common input props](./Inputs.md#common-input-props). @@ -585,26 +628,35 @@ const choices = [ { _id: 123, full_name: 'Leo Tolstoi', sex: 'M' }, { _id: 456, full_name: 'Jane Austen', sex: 'F' }, ]; - +; ``` `optionText` also accepts a function, so you can shape the option text at will: ```jsx const choices = [ - { id: 123, first_name: 'Leo', last_name: 'Tolstoi' }, - { id: 456, first_name: 'Jane', last_name: 'Austen' }, + { id: 123, first_name: 'Leo', last_name: 'Tolstoi' }, + { id: 456, first_name: 'Jane', last_name: 'Austen' }, ]; const optionRenderer = choice => `${choice.first_name} ${choice.last_name}`; - +; ``` `optionText` also accepts a custom Component. However, as the underlying Autocomplete component requires that the current selection is a string, if you opt for a Component, you must pass a function as the `inputText` prop. This function should return text representation of the current selection: ```jsx const choices = [ - { id: 123, first_name: 'Leo', last_name: 'Tolstoi', avatar:'/pengouin' }, - { id: 456, first_name: 'Jane', last_name: 'Austen', avatar:'/panda' }, + { id: 123, first_name: 'Leo', last_name: 'Tolstoi', avatar: '/pengouin' }, + { id: 456, first_name: 'Jane', last_name: 'Austen', avatar: '/panda' }, ]; const OptionRenderer = choice => ( @@ -618,15 +670,15 @@ const inputText = choice => `${choice.first_name} ${choice.last_name}`; choices={choices} optionText={} inputText={inputText} -/> +/>; ``` The choices are translated by default, so you can use translation identifiers as choices: ```jsx const choices = [ - { id: 'M', name: 'myroot.gender.male' }, - { id: 'F', name: 'myroot.gender.female' }, + { id: 'M', name: 'myroot.gender.male' }, + { id: 'F', name: 'myroot.gender.female' }, ]; ``` @@ -634,7 +686,7 @@ However, in some cases (e.g. inside a ``), you may not want the In that case, set the `translateChoice` prop to `false`. ```jsx - + ``` If you want to limit the initial choices shown to the current value only, you can set the `limitChoicesToValue` prop. @@ -645,11 +697,16 @@ Ex. ` { return val.trim().le `` renders a [material-ui `` component](https://material-ui.com/api/text-field/). Use the `options` attribute to override any of the `` attributes: {% raw %} + ```jsx - + ``` + {% endraw %} **Tip**: If you want to populate the `choices` attribute with a list of related records, you should decorate `` with [``](#referenceinput), and leave the `choices` empty: @@ -659,21 +716,27 @@ import { AutocompleteInput, ReferenceInput } from 'react-admin'; - +; ``` Lastly, would you need to override the props of the suggestion's container (a `Popper` element), you can specify them using the `options.suggestionsContainerProps`. For example: {% raw %} + ```jsx - + ``` + {% endraw %} -**Tip**: `` is a stateless component, so it only allows to *filter* the list of choices, not to *extend* it. If you need to populate the list of choices based on the result from a `fetch` call (and if [``](#referenceinput) doesn't cover your need), you'll have to [write your own Input component](#writing-your-own-input-component) based on material-ui `` component. +**Tip**: `` is a stateless component, so it only allows to _filter_ the list of choices, not to _extend_ it. If you need to populate the list of choices based on the result from a `fetch` call (and if [``](#referenceinput) doesn't cover your need), you'll have to [write your own Input component](#writing-your-own-input-component) based on material-ui `` component. #### Creating New Choices @@ -682,10 +745,11 @@ The `` can allow users to create a new choice if either the ` Use the `onCreate` prop when you only require users to provide a simple string and a `prompt` is enough. You can return either the new choice directly or a Promise resolving to the new choice. {% raw %} + ```js import { AutocompleteInput, Create, SimpleForm, TextInput } from 'react-admin'; -const PostCreate = (props) => { +const PostCreate = props => { const categories = [ { name: 'Tech', id: 'tech' }, { name: 'Lifestyle', id: 'lifestyle' }, @@ -697,7 +761,10 @@ const PostCreate = (props) => { { const newCategoryName = prompt('Enter a new category'); - const newCategory = { id: newCategoryName.toLowerCase(), name: newCategoryName }; + const newCategory = { + id: newCategoryName.toLowerCase(), + name: newCategoryName, + }; categories.push(newCategory); return newCategory; }} @@ -707,13 +774,15 @@ const PostCreate = (props) => {
); -} +}; ``` + {% endraw %} Use the `create` prop when you want a more polished or complex UI. For example a Material UI `` asking for multiple fields because the choices are from a referenced resource. {% raw %} + ```js import { AutocompleteInput, @@ -721,7 +790,7 @@ import { ReferenceInput, SimpleForm, TextInput, - useCreateSuggestionContext + useCreateSuggestionContext, } from 'react-admin'; import { @@ -734,7 +803,7 @@ import { TextField, } from '@material-ui/core'; -const PostCreate = (props) => { +const PostCreate = props => { return ( @@ -745,14 +814,14 @@ const PostCreate = (props) => { ); -} +}; const CreateCategory = () => { const { filter, onCancel, onCreate } = useCreateSuggestionContext(); const [value, setValue] = React.useState(filter || ''); const [create] = useCreate('categories'); - const handleSubmit = (event) => { + const handleSubmit = event => { event.preventDefault(); create( { @@ -791,6 +860,7 @@ const CreateCategory = () => { ); }; ``` + {% endraw %} #### CSS API @@ -802,10 +872,9 @@ const CreateCategory = () => { To override the style of all instances of `` using the [material-ui style overrides](https://material-ui.com/customization/globals/#css), use the `RaAutocompleteInput` key. - ### `` -If you want to let the user choose a value among a list of possible values that are always shown (instead of hiding them behind a dropdown list, as in [``](#selectinput)), `` is the right component. +If you want to let the user choose a value among a list of possible values that are always shown (instead of hiding them behind a dropdown list, as in [``](#selectinput)), `` is the right component. ![RadioButtonGroupInput](./img/radio-button-group-input.gif) @@ -814,11 +883,14 @@ Set the `choices` attribute to determine the options (with `id`, `name` tuples): ```jsx import { RadioButtonGroupInput } from 'react-admin'; - +; ``` #### Properties @@ -843,54 +915,80 @@ const choices = [ { _id: 123, full_name: 'Leo Tolstoi', sex: 'M' }, { _id: 456, full_name: 'Jane Austen', sex: 'F' }, ]; - +; ``` `optionText` also accepts a function, so you can shape the option text at will: ```jsx const choices = [ - { id: 123, first_name: 'Leo', last_name: 'Tolstoi' }, - { id: 456, first_name: 'Jane', last_name: 'Austen' }, + { id: 123, first_name: 'Leo', last_name: 'Tolstoi' }, + { id: 456, first_name: 'Jane', last_name: 'Austen' }, ]; const optionRenderer = choice => `${choice.first_name} ${choice.last_name}`; - +; ``` `optionText` also accepts a React Element, that will be cloned and receive the related choice as the `record` prop. You can use Field components there. ```jsx const choices = [ - { id: 123, first_name: 'Leo', last_name: 'Tolstoi' }, - { id: 456, first_name: 'Jane', last_name: 'Austen' }, + { id: 123, first_name: 'Leo', last_name: 'Tolstoi' }, + { id: 456, first_name: 'Jane', last_name: 'Austen' }, ]; -const FullNameField = ({ record }) => {record.first_name} {record.last_name}; -}/> +const FullNameField = ({ record }) => ( + + {record.first_name} {record.last_name} + +); +} +/>; ``` The choices are translated by default, so you can use translation identifiers as choices: ```jsx const choices = [ - { id: 'M', name: 'myroot.gender.male' }, - { id: 'F', name: 'myroot.gender.female' }, + { id: 'M', name: 'myroot.gender.male' }, + { id: 'F', name: 'myroot.gender.female' }, ]; ``` However, in some cases (e.g. inside a ``), you may not want the choice to be translated. In that case, set the `translateChoice` prop to `false`. ```jsx - + ``` Lastly, use the `options` attribute if you want to override any of Material UI's `` attributes: {% raw %} + ```jsx - + ``` + {% endraw %} Refer to [Material UI RadioGroup documentation](https://material-ui.com/api/radio-group) for more details. @@ -902,21 +1000,20 @@ import { RadioButtonGroupInput, ReferenceInput } from 'react-admin'; - +; ``` #### CSS API -| Rule name | Description | -| ---------- | ------------------------------------------------------------- | -| `label` | Applied to the underlying Material UI's `FormLabel` component | +| Rule name | Description | +| --------- | ------------------------------------------------------------- | +| `label` | Applied to the underlying Material UI's `FormLabel` component | To override the style of all instances of `` using the [material-ui style overrides](https://material-ui.com/customization/globals/#css), use the `RaRadioButtonGroupInput` key. - ### `` -To let users choose a value in a list using a dropdown, use ``. It renders using [Material ui's ``](https://material-ui.com/api/select). ![SelectInput](./img/select-input.gif) @@ -925,28 +1022,31 @@ Set the `choices` attribute to determine the options (with `id`, `name` tuples): ```jsx import { SelectInput } from 'react-admin'; - +; ``` #### Properties -| Prop | Required | Type | Default | Description | -| ----------------- | -------- | -------------------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------- | -| `allowEmpty` | Optional | `boolean` | `false` | If true, the first option is an empty one | -| `choices` | Required | `Object[]` | - | List of items to show as options | -| `create` | Optional | `Element` | `-` | A React Element to render when users want to create a new choice | -| `createLabel` | Optional | `string` | `ra.action.create` | The label for the menu item allowing users to create a new choice. Used when the filter is empty | -| `emptyText` | Optional | `string` | '' | The text to display for the empty option | -| `onCreate` | Optional | `Function` | `-` | A function called with the current filter value when users choose to create a new choice. | -| `options` | Optional | `Object` | - | Props to pass to the underlying `` element | -| `optionText` | Optional | `string` | `Function` | `name` | Field name of record to display in the suggestion item or function which accepts the current record as argument (`record => {string}`) | -| `optionValue` | Optional | `string` | `id` | Field name of record containing the value to use as input value | -| `resettable` | Optional | `boolean` | `false` | If `true`, display a button to reset the changes in this input value | -| `translateChoice` | Optional | `boolean` | `true` | Whether the choices should be translated | +| Prop | Required | Type | Default | Description | +| ----------------- | -------- | -------------------------- | ------------------ | -------------------------------------------------------------------------------------------------------------------------------------- | +| `allowEmpty` | Optional | `boolean` | `false` | If true, the first option is an empty one | +| `choices` | Required | `Object[]` | - | List of items to show as options | +| `create` | Optional | `Element` | `-` | A React Element to render when users want to create a new choice | +| `createLabel` | Optional | `string` | `ra.action.create` | The label for the menu item allowing users to create a new choice. Used when the filter is empty | +| `emptyText` | Optional | `string` | '' | The text to display for the empty option | +| `onCreate` | Optional | `Function` | `-` | A function called with the current filter value when users choose to create a new choice. | +| `options` | Optional | `Object` | - | Props to pass to the underlying `` element | +| `optionText` | Optional | `string` | `Function` | `name` | Field name of record to display in the suggestion item or function which accepts the current record as argument (`record => {string}`) | +| `optionValue` | Optional | `string` | `id` | Field name of record containing the value to use as input value | +| `resettable` | Optional | `boolean` | `false` | If `true`, display a button to reset the changes in this input value | +| `translateChoice` | Optional | `boolean` | `true` | Whether the choices should be translated | `` also accepts the [common input props](./Inputs.md#common-input-props). @@ -959,54 +1059,76 @@ const choices = [ { _id: 123, full_name: 'Leo Tolstoi', sex: 'M' }, { _id: 456, full_name: 'Jane Austen', sex: 'F' }, ]; - +; ``` `optionText` also accepts a function, so you can shape the option text at will: ```jsx const choices = [ - { id: 123, first_name: 'Leo', last_name: 'Tolstoi' }, - { id: 456, first_name: 'Jane', last_name: 'Austen' }, + { id: 123, first_name: 'Leo', last_name: 'Tolstoi' }, + { id: 456, first_name: 'Jane', last_name: 'Austen' }, ]; const optionRenderer = choice => `${choice.first_name} ${choice.last_name}`; - +; ``` `optionText` also accepts a React Element, that will be cloned and receive the related choice as the `record` prop. You can use Field components there. ```jsx const choices = [ - { id: 123, first_name: 'Leo', last_name: 'Tolstoi' }, - { id: 456, first_name: 'Jane', last_name: 'Austen' }, + { id: 123, first_name: 'Leo', last_name: 'Tolstoi' }, + { id: 456, first_name: 'Jane', last_name: 'Austen' }, ]; -const FullNameField = ({ record }) => {record.first_name} {record.last_name}; -}/> +const FullNameField = ({ record }) => ( + + {record.first_name} {record.last_name} + +); +} +/>; ``` Enabling the `allowEmpty` props adds an empty choice (with a default `''` value, which you can overwrite with the `emptyValue` prop) on top of the options. You can furthermore customize the `MenuItem` for the empty choice by using the `emptyText` prop, which can receive either a string or a React Element, which doesn't receive any props. ```jsx - + ``` The choices are translated by default, so you can use translation identifiers as choices: ```jsx const choices = [ - { id: 'M', name: 'myroot.gender.male' }, - { id: 'F', name: 'myroot.gender.female' }, + { id: 'M', name: 'myroot.gender.male' }, + { id: 'F', name: 'myroot.gender.female' }, ]; ``` However, in some cases, you may not want the choice to be translated. In that case, set the `translateChoice` prop to `false`. ```jsx - + ``` Note that `translateChoice` is set to `false` when `` is a child of ``. @@ -1014,11 +1136,16 @@ Note that `translateChoice` is set to `false` when `` is a child of Lastly, use the `options` attribute if you want to override any of Material UI's `` attributes: {% raw %} + ```jsx - + ``` + {% endraw %} Refer to [Material UI Select documentation](https://material-ui.com/api/select) for more details. @@ -1030,7 +1157,7 @@ import { SelectInput, ReferenceInput } from 'react-admin'; - +; ``` If, instead of showing choices as a dropdown list, you prefer to display them as a list of radio buttons, try the [``](#radiobuttongroupinput). And if the list is too big, prefer the [``](#autocompleteinput). @@ -1047,7 +1174,12 @@ const choices = [ { _id: 456, full_name: 'Jane Austen', sex: 'F' }, { _id: 1, full_name: 'System Administrator', sex: 'F', disabled: true }, ]; - +; ``` You can use a custom field name by setting `disableValue` prop: @@ -1058,7 +1190,13 @@ const choices = [ { _id: 456, full_name: 'Jane Austen', sex: 'F' }, { _id: 987, full_name: 'Jack Harden', sex: 'M', not_available: true }, ]; - +; ``` #### Creating New Choices @@ -1068,10 +1206,11 @@ The `` can allow users to create a new choice if either the `create Use the `onCreate` prop when you only require users to provide a simple string and a `prompt` is enough. You can return either the new choice directly or a Promise resolving to the new choice. {% raw %} + ```js import { SelectInput, Create, SimpleForm, TextInput } from 'react-admin'; -const PostCreate = (props) => { +const PostCreate = props => { const categories = [ { name: 'Tech', id: 'tech' }, { name: 'Lifestyle', id: 'lifestyle' }, @@ -1083,7 +1222,10 @@ const PostCreate = (props) => { { const newCategoryName = prompt('Enter a new category'); - const newCategory = { id: newCategoryName.toLowerCase(), name: newCategoryName }; + const newCategory = { + id: newCategoryName.toLowerCase(), + name: newCategoryName, + }; categories.push(newCategory); return newCategory; }} @@ -1093,13 +1235,15 @@ const PostCreate = (props) => { ); -} +}; ``` + {% endraw %} Use the `create` prop when you want a more polished or complex UI. For example a Material UI `` asking for multiple fields because the choices are from a referenced resource. {% raw %} + ```js import { SelectInput, @@ -1107,7 +1251,7 @@ import { ReferenceInput, SimpleForm, TextInput, - useCreateSuggestionContext + useCreateSuggestionContext, } from 'react-admin'; import { @@ -1120,7 +1264,7 @@ import { TextField, } from '@material-ui/core'; -const PostCreate = (props) => { +const PostCreate = props => { return ( @@ -1131,14 +1275,14 @@ const PostCreate = (props) => { ); -} +}; const CreateCategory = () => { const { filter, onCancel, onCreate } = useCreateSuggestionContext(); const [value, setValue] = React.useState(filter || ''); const [create] = useCreate('categories'); - const handleSubmit = (event) => { + const handleSubmit = event => { event.preventDefault(); create( { @@ -1177,13 +1321,14 @@ const CreateCategory = () => { ); }; ``` + {% endraw %} #### CSS API -| Rule name | Description | -| --------------- | --------------------------------------------------------- | -| `input` | Applied to the underlying `ResettableTextField` component | +| Rule name | Description | +| --------- | --------------------------------------------------------- | +| `input` | Applied to the underlying `ResettableTextField` component | To override the style of all instances of `` using the [material-ui style overrides](https://material-ui.com/customization/globals/#css), use the `RaSelectInput` key. @@ -1196,84 +1341,113 @@ To edit arrays of data embedded inside a record, `` creates a list o ![ArrayInput](./img/array-input.gif) ```jsx -import { ArrayInput, SimpleFormIterator, DateInput, TextInput } from 'react-admin'; +import { + ArrayInput, + SimpleFormIterator, + DateInput, + TextInput, +} from 'react-admin'; - +; ``` `` allows editing of embedded arrays, like the `authors` field in the following `post` record: ```json { - "id": 123, - "authors": [ + "id": 123, + "authors": [ { "user_id": 123, - "role": "head_writer", + "role": "head_writer" }, { "user_id": 456, - "url": "co_writer", + "url": "co_writer" } - ] + ] } ``` #### Usage -`` expects a single child, which must be a *form iterator* component. A form iterator is a component accepting a `fields` object as passed by [react-final-form-array](https://github.com/final-form/react-final-form-arrays#fieldarrayrenderprops), and defining a layout for an array of fields. For instance, the `` component displays an array of react-admin Inputs in an unordered list (`
    `), one sub-form by list item (`
  • `). It also provides controls for adding and removing a sub-record (a backlink in this example). +`` expects a single child, which must be a _form iterator_ component. A form iterator is a component accepting a `fields` object as passed by [react-final-form-array](https://github.com/final-form/react-final-form-arrays#fieldarrayrenderprops), and defining a layout for an array of fields. For instance, the `` component displays an array of react-admin Inputs in an unordered list (`
      `), one sub-form by list item (`
    • `). It also provides controls for adding and removing a sub-record (a backlink in this example). You can pass `disableAdd` and `disableRemove` as props of `SimpleFormIterator`, to disable `ADD` and `REMOVE` button respectively. Default value of both is `false`. ```jsx -import { ArrayInput, SimpleFormIterator, DateInput, TextInput } from 'react-admin'; +import { + ArrayInput, + SimpleFormIterator, + DateInput, + TextInput, +} from 'react-admin'; - + - +; ``` You can also use `addButton` and `removeButton` props to pass your custom add and remove buttons to `SimpleFormIterator`. ```jsx -import { ArrayInput, SimpleFormIterator, DateInput, TextInput } from 'react-admin'; +import { + ArrayInput, + SimpleFormIterator, + DateInput, + TextInput, +} from 'react-admin'; - } removeButton={}> + } + removeButton={} + > - +; ``` Furthermore, if you want to customize the label displayed for each item, you can pass a function to `` via the `getItemLabel` prop. ```jsx -import { ArrayInput, SimpleFormIterator, DateInput, TextInput } from 'react-admin'; +import { + ArrayInput, + SimpleFormIterator, + DateInput, + TextInput, +} from 'react-admin'; - `${index + 1}. link`}> + `${index + 1}. link`}> - +; ``` **Note**: `` only accepts `Input` components as children. If you want to use some `Fields` instead, you have to use a `` to get the correct source, as follows: ```jsx -import { ArrayInput, SimpleFormIterator, DateInput, TextInput, FormDataConsumer } from 'react-admin'; +import { + ArrayInput, + SimpleFormIterator, + DateInput, + TextInput, + FormDataConsumer, +} from 'react-admin'; - + {({ getSource, scopedFormData }) => { @@ -1286,10 +1460,10 @@ import { ArrayInput, SimpleFormIterator, DateInput, TextInput, FormDataConsumer }} - +; ``` -`` also accepts the [common input props](./Inputs.md#common-input-props) (except `format` and `parse`). +`` also accepts the [common input props](./Inputs.md#common-input-props) (except `format` and `parse`). **Important**: Note that asynchronous validators are not supported on the `` component due to a limitation of [react-final-form-arrays](https://github.com/final-form/react-final-form-arrays). @@ -1305,32 +1479,35 @@ Set the `choices` attribute to determine the options list (with `id`, `name` tup ```jsx import { AutocompleteArrayInput } from 'react-admin'; - +; ``` #### Properties -| Prop | Required | Type | Default | Description | -| ------------------------- | -------- | -------------------------- | ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `allowEmpty` | Optional | `boolean` | `false` | If `true`, the first option is an empty one | -| `allowDuplicates` | Optional | `boolean` | `false` | If `true`, the options can be selected several times | -| `create` | Optional | `Element` | `-` | A React Element to render when users want to create a new choice | -| `createLabel` | Optional | `string` | `ra.action.create` | The label for the menu item allowing users to create a new choice. Used when the filter is empty | -| `createItemLabel` | Optional | `string` | `ra.action.create_item` | The label for the menu item allowing users to create a new choice. Used when the filter is not empty | -| `debounce` | Optional | `number` | `250` | The delay to wait before calling the setFilter function injected when used in a ReferenceInput. | -| `choices` | Required | `Object[]` | - | List of items to autosuggest | -| `matchSuggestion` | Optional | `Function` | - | Required if `optionText` is a React element. Function returning a boolean indicating whether a choice matches the filter. `(filter, choice) => boolean` | -| `onCreate` | Optional | `Function` | `-` | A function called with the current filter value when users choose to create a new choice. | -| `optionValue` | Optional | `string` | `id` | Field name of record containing the value to use as input value | -| `optionText` | Optional | `string` | `Function` | `name` | Field name of record to display in the suggestion item or function which accepts the current record as argument (`record => {string}`) | -| `setFilter` | Optional | `Function` | `null` | A callback to inform the `searchText` has changed and new `choices` can be retrieved based on this `searchText`. Signature `searchText => void`. This function is automatically setup when using `ReferenceInput`. | -| `shouldRenderSuggestions` | Optional | `Function` | `() => true` | A function that returns a `boolean` to determine whether or not suggestions are rendered. Use this when working with large collections of data to improve performance and user experience. This function is passed into the underlying react-autosuggest component. Ex.`(value) => value.trim() > 2` | -| `source` | Required | `string` | - | Name of field to edit, its type should match the type retrieved from `optionValue` | -| `suggestionLimit` | Optional | `number` | `null` | Limits the numbers of suggestions that are shown in the dropdown list | +| Prop | Required | Type | Default | Description | +| ------------------------- | -------- | -------------------------- | ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `allowEmpty` | Optional | `boolean` | `false` | If `true`, the first option is an empty one | +| `allowDuplicates` | Optional | `boolean` | `false` | If `true`, the options can be selected several times | +| `create` | Optional | `Element` | `-` | A React Element to render when users want to create a new choice | +| `createLabel` | Optional | `string` | `ra.action.create` | The label for the menu item allowing users to create a new choice. Used when the filter is empty | +| `createItemLabel` | Optional | `string` | `ra.action.create_item` | The label for the menu item allowing users to create a new choice. Used when the filter is not empty | +| `debounce` | Optional | `number` | `250` | The delay to wait before calling the setFilter function injected when used in a ReferenceInput. | +| `choices` | Required | `Object[]` | - | List of items to autosuggest | +| `matchSuggestion` | Optional | `Function` | - | Required if `optionText` is a React element. Function returning a boolean indicating whether a choice matches the filter. `(filter, choice) => boolean` | +| `onCreate` | Optional | `Function` | `-` | A function called with the current filter value when users choose to create a new choice. | +| `optionValue` | Optional | `string` | `id` | Field name of record containing the value to use as input value | +| `optionText` | Optional | `string` | `Function` | `name` | Field name of record to display in the suggestion item or function which accepts the current record as argument (`record => {string}`) | +| `setFilter` | Optional | `Function` | `null` | A callback to inform the `searchText` has changed and new `choices` can be retrieved based on this `searchText`. Signature `searchText => void`. This function is automatically setup when using `ReferenceInput`. | +| `shouldRenderSuggestions` | Optional | `Function` | `() => true` | A function that returns a `boolean` to determine whether or not suggestions are rendered. Use this when working with large collections of data to improve performance and user experience. This function is passed into the underlying react-autosuggest component. Ex.`(value) => value.trim() > 2` | +| `source` | Required | `string` | - | Name of field to edit, its type should match the type retrieved from `optionValue` | +| `suggestionLimit` | Optional | `number` | `null` | Limits the numbers of suggestions that are shown in the dropdown list | `` also accepts the [common input props](./Inputs.md#common-input-props). @@ -1340,22 +1517,19 @@ import { AutocompleteArrayInput } from 'react-admin'; ```json { - "id": 123, - "tags": ["lifestyle", "photography"] + "id": 123, + "tags": ["lifestyle", "photography"] } ``` -When working with a field that contains an array of *objects*, use `parse` and `format` to turn the value into an array of scalar values. +When working with a field that contains an array of _objects_, use `parse` and `format` to turn the value into an array of scalar values. So for instance, for editing the `tags` field of records looking like the following: ```json { - "id": 123, - "tags": [ - { "id": "lifestyle" }, - { "id": "photography" } - ] + "id": 123, + "tags": [{ "id": "lifestyle" }, { "id": "photography" }] } ``` @@ -1364,18 +1538,16 @@ You should use the following syntax: ```jsx import { AutocompleteArrayInput } from 'react-admin'; - - value && value.map(v => ({ id: v })) - } + parse={value => value && value.map(v => ({ id: v }))} format={value => value && value.map(v => v.id)} choices={[ { id: 'programming', name: 'Programming' }, { id: 'lifestyle', name: 'Lifestyle' }, { id: 'photography', name: 'Photography' }, ]} -/> +/>; ``` You can customize the properties to use for the option name and value, thanks to the `optionText` and `optionValue` attributes: @@ -1385,33 +1557,46 @@ const choices = [ { _id: 123, full_name: 'Leo Tolstoi', sex: 'M' }, { _id: 456, full_name: 'Jane Austen', sex: 'F' }, ]; - +; ``` `optionText` also accepts a function, so you can shape the option text at will: ```jsx const choices = [ - { id: 123, first_name: 'Leo', last_name: 'Tolstoi' }, - { id: 456, first_name: 'Jane', last_name: 'Austen' }, + { id: 123, first_name: 'Leo', last_name: 'Tolstoi' }, + { id: 456, first_name: 'Jane', last_name: 'Austen' }, ]; const optionRenderer = choice => `${choice.first_name} ${choice.last_name}`; - +; ``` The choices are translated by default, so you can use translation identifiers as choices: ```jsx const choices = [ - { id: 'M', name: 'myroot.gender.male' }, - { id: 'F', name: 'myroot.gender.female' }, + { id: 'M', name: 'myroot.gender.male' }, + { id: 'F', name: 'myroot.gender.female' }, ]; ``` However, in some cases (e.g. inside a ``), you may not want the choice to be translated. In that case, set the `translateChoice` prop to `false`. ```jsx - + ``` When dealing with a large amount of `choices` you may need to limit the number of suggestions that are rendered in order to maintain usable performance. The `shouldRenderSuggestions` is an optional prop that allows you to set conditions on when to render suggestions. An easy way to improve performance would be to skip rendering until the user has entered 2 or 3 characters in the search box. This lowers the result set significantly, and might be all you need (depending on your data set). @@ -1420,11 +1605,16 @@ Ex. ` { return val.trim Lastly, `` renders a [material-ui `` component](https://material-ui.com/api/text-field/). Use the `options` attribute to override any of the `` attributes: {% raw %} + ```jsx - + ``` + {% endraw %} **Tip**: Like many other inputs, `` accept a `fullWidth` prop. @@ -1436,21 +1626,27 @@ import { AutocompleteArrayInput, ReferenceArrayInput } from 'react-admin'; - +; ``` If you need to override the props of the suggestion's container (a `Popper` element), you can specify them using the `options.suggestionsContainerProps`. For example: {% raw %} + ```jsx - + ``` + {% endraw %} -**Tip**: `` is a stateless component, so it only allows to *filter* the list of choices, not to *extend* it. If you need to populate the list of choices based on the result from a `fetch` call (and if [``](#referencearrayinput) doesn't cover your need), you'll have to [write your own Input component](#writing-your-own-input-component) based on [material-ui-chip-input](https://github.com/TeamWertarbyte/material-ui-chip-input). +**Tip**: `` is a stateless component, so it only allows to _filter_ the list of choices, not to _extend_ it. If you need to populate the list of choices based on the result from a `fetch` call (and if [``](#referencearrayinput) doesn't cover your need), you'll have to [write your own Input component](#writing-your-own-input-component) based on [material-ui-chip-input](https://github.com/TeamWertarbyte/material-ui-chip-input). **Tip**: React-admin's `` has only a capital A, while material-ui's `` has a capital A and a capital C. Don't mix up the components! @@ -1461,10 +1657,16 @@ The `` can allow users to create a new choice if either Use the `onCreate` prop when you only require users to provide a simple string and a `prompt` is enough. You can return either the new choice directly or a Promise resolving to the new choice. {% raw %} + ```js -import { AutocompleteArrayInput, Create, SimpleForm, TextInput } from 'react-admin'; +import { + AutocompleteArrayInput, + Create, + SimpleForm, + TextInput, +} from 'react-admin'; -const PostCreate = (props) => { +const PostCreate = props => { const tags = [ { name: 'Tech', id: 'tech' }, { name: 'Lifestyle', id: 'lifestyle' }, @@ -1476,7 +1678,10 @@ const PostCreate = (props) => { { const newTagName = prompt('Enter a new tag'); - const newTag = { id: newTagName.toLowerCase(), name: newTagName }; + const newTag = { + id: newTagName.toLowerCase(), + name: newTagName, + }; categories.push(newTag); return newTag; }} @@ -1486,13 +1691,15 @@ const PostCreate = (props) => { ); -} +}; ``` + {% endraw %} Use the `create` prop when you want a more polished or complex UI. For example a Material UI `` asking for multiple fields because the choices are from a referenced resource. {% raw %} + ```js import { AutocompleteArrayInput, @@ -1500,7 +1707,7 @@ import { ReferenceArrayInput, SimpleForm, TextInput, - useCreateSuggestionContext + useCreateSuggestionContext, } from 'react-admin'; import { @@ -1513,7 +1720,7 @@ import { TextField, } from '@material-ui/core'; -const PostCreate = (props) => { +const PostCreate = props => { return ( @@ -1524,14 +1731,14 @@ const PostCreate = (props) => { ); -} +}; const CreateTag = () => { const { filter, onCancel, onCreate } = useCreateSuggestionContext(); const [value, setValue] = React.useState(filter || ''); const [create] = useCreate('tags'); - const handleSubmit = (event) => { + const handleSubmit = event => { event.preventDefault(); create( { @@ -1570,14 +1777,15 @@ const CreateTag = () => { ); }; ``` + {% endraw %} #### CSS API | Rule name | Description | -| ---------------------- | --------------------------------------------------------------------------------------------------------------------------- | +| ----------------------- | --------------------------------------------------------------------------------------------------------------------------- | | `container` | Applied to the container of the underlying Material UI's `TextField` component input | -| `suggestionsContainer` | Applied to the suggestions container | +| `suggestionsContainer` | Applied to the suggestions container | | `chip` | Applied to each Material UI's `Chip` component used as selected item | | `chipContainerFilled` | Applied to each container of each Material UI's `Chip` component used as selected item when `variant` prop is `filled` | | `chipContainerOutlined` | Applied to each container of each `Chip` component used as selected item when `variant` prop is `outlined` | @@ -1589,7 +1797,7 @@ To override the style of all instances of `` using the [ ### `` -If you want to let the user choose multiple values among a list of possible values by showing them all, `` is the right component. +If you want to let the user choose multiple values among a list of possible values by showing them all, `` is the right component. ![CheckboxGroupInput](./img/checkbox-group-input.png) @@ -1598,17 +1806,20 @@ Set the `choices` attribute to determine the options (with `id`, `name` tuples): ```jsx import { CheckboxGroupInput } from 'react-admin'; - +; ``` #### Properties | Prop | Required | Type | Default | Description | -| ------------- | -------- | -------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------- | +| ------------- | -------- | -------------------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------- | | `choices` | Required | `Object[]` | - | List of choices | | `optionText` | Optional | `string` | `Function` | `name` | Field name of record to display in the suggestion item or function which accepts the correct record as argument (`record => {string}`) | | `optionValue` | Optional | `string` | `id` | Field name of record containing the value to use as input value | @@ -1627,29 +1838,46 @@ const choices = [ { _id: 123, full_name: 'Leo Tolstoi', sex: 'M' }, { _id: 456, full_name: 'Jane Austen', sex: 'F' }, ]; - +; ``` `optionText` also accepts a function, so you can shape the option text at will: ```jsx const choices = [ - { id: 123, first_name: 'Leo', last_name: 'Tolstoi' }, - { id: 456, first_name: 'Jane', last_name: 'Austen' }, + { id: 123, first_name: 'Leo', last_name: 'Tolstoi' }, + { id: 456, first_name: 'Jane', last_name: 'Austen' }, ]; const optionRenderer = choice => `${choice.first_name} ${choice.last_name}`; - +; ``` `optionText` also accepts a React Element, that will be cloned and receive the related choice as the `record` prop. You can use Field components there. ```jsx const choices = [ - { id: 123, first_name: 'Leo', last_name: 'Tolstoi' }, - { id: 456, first_name: 'Jane', last_name: 'Austen' }, + { id: 123, first_name: 'Leo', last_name: 'Tolstoi' }, + { id: 456, first_name: 'Jane', last_name: 'Austen' }, ]; -const FullNameField = ({ record }) => {record.first_name} {record.last_name}; -}/> +const FullNameField = ({ record }) => ( + + {record.first_name} {record.last_name} + +); +} +/>; ``` The choices are translated by default, so you can use translation identifiers as choices: @@ -1665,28 +1893,33 @@ const choices = [ However, in some cases (e.g. inside a ``), you may not want the choice to be translated. In that case, set the `translateChoice` prop to `false`. ```jsx - + ``` Lastly, use the `options` attribute if you want to override any of Material UI's `` attributes: {% raw %} + ```jsx import { FavoriteBorder, Favorite } from '@material-ui/icons'; -, - checkedIcon: -}} /> +, + checkedIcon: , + }} +/>; ``` + {% endraw %} #### CSS API -| Rule name | Description | -| ---------- | ------------------------------------------------------------- | -| `root` | Applied to the root element | -| `label` | Applied to the underlying Material UI's `FormLabel` component | +| Rule name | Description | +| --------- | ------------------------------------------------------------- | +| `root` | Applied to the root element | +| `label` | Applied to the underlying Material UI's `FormLabel` component | To override the style of all instances of `` using the [material-ui style overrides](https://material-ui.com/customization/globals/#css), use the `RaCheckboxGroupInput` key. @@ -1702,32 +1935,32 @@ import { DualListInput } from '@react-admin/ra-relationships'; - +; ``` Check [the `ra-relationships` documentation](https://marmelab.com/ra-enterprise/modules/ra-relationships) for more details. ### `` -To let users choose several values in a list using a dropdown, use ``. It renders using [Material ui's ``](https://material-ui.com/api/select). ![SelectArrayInput](./img/select-array-input.gif) #### Properties -| Prop | Required | Type | Default | Description | -| ----------------- | -------- | -------------------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------- | -| `allowEmpty` | Optional | `boolean` | `false` | If true, the first option is an empty one | -| `choices` | Required | `Object[]` | - | List of items to show as options | -| `create` | Optional | `Element` | `-` | A React Element to render when users want to create a new choice | -| `createLabel` | Optional | `string` | `ra.action.create` | The label for the menu item allowing users to create a new choice. Used when the filter is empty | -| `emptyText` | Optional | `string` | '' | The text to display for the empty option | -| `onCreate` | Optional | `Function` | `-` | A function called with the current filter value when users choose to create a new choice. | -| `options` | Optional | `Object` | - | Props to pass to the underlying `` element | -| `optionText` | Optional | `string` | `Function` | `name` | Field name of record to display in the suggestion item or function which accepts the current record as argument (`record => {string}`) | -| `optionValue` | Optional | `string` | `id` | Field name of record containing the value to use as input value | -| `resettable` | Optional | `boolean` | `false` | If `true`, display a button to reset the changes in this input value | -| `translateChoice` | Optional | `boolean` | `true` | Whether the choices should be translated | +| Prop | Required | Type | Default | Description | +| ----------------- | -------- | -------------------------- | ------------------ | -------------------------------------------------------------------------------------------------------------------------------------- | +| `allowEmpty` | Optional | `boolean` | `false` | If true, the first option is an empty one | +| `choices` | Required | `Object[]` | - | List of items to show as options | +| `create` | Optional | `Element` | `-` | A React Element to render when users want to create a new choice | +| `createLabel` | Optional | `string` | `ra.action.create` | The label for the menu item allowing users to create a new choice. Used when the filter is empty | +| `emptyText` | Optional | `string` | '' | The text to display for the empty option | +| `onCreate` | Optional | `Function` | `-` | A function called with the current filter value when users choose to create a new choice. | +| `options` | Optional | `Object` | - | Props to pass to the underlying `` element | +| `optionText` | Optional | `string` | `Function` | `name` | Field name of record to display in the suggestion item or function which accepts the current record as argument (`record => {string}`) | +| `optionValue` | Optional | `string` | `id` | Field name of record containing the value to use as input value | +| `resettable` | Optional | `boolean` | `false` | If `true`, display a button to reset the changes in this input value | +| `translateChoice` | Optional | `boolean` | `true` | Whether the choices should be translated | `` also accepts the [common input props](./Inputs.md#common-input-props). @@ -1738,13 +1971,17 @@ Set the `choices` attribute to determine the options (with `id`, `name` tuples): ```jsx import { SelectArrayInput } from 'react-admin'; - +; ``` You can also customize the properties to use for the option name and value, @@ -1752,35 +1989,44 @@ thanks to the `optionText` and `optionValue` attributes. ```jsx const choices = [ - { _id: '1', name: 'Book', plural_name: 'Books' }, - { _id: '2', name: 'Video', plural_name: 'Videos' }, - { _id: '3', name: 'Audio', plural_name: 'Audios' }, + { _id: '1', name: 'Book', plural_name: 'Books' }, + { _id: '2', name: 'Video', plural_name: 'Videos' }, + { _id: '3', name: 'Audio', plural_name: 'Audios' }, ]; - +; ``` `optionText` also accepts a function, so you can shape the option text at will: ```jsx const choices = [ - { id: '1', name: 'Book', quantity: 23 }, - { id: '2', name: 'Video', quantity: 56 }, - { id: '3', name: 'Audio', quantity: 12 }, + { id: '1', name: 'Book', quantity: 23 }, + { id: '2', name: 'Video', quantity: 56 }, + { id: '3', name: 'Audio', quantity: 12 }, ]; const optionRenderer = choice => `${choice.name} (${choice.quantity})`; - +; ``` The choices are translated by default, so you can use translation identifiers as choices: ```js const choices = [ - { id: 'books', name: 'myroot.category.books' }, - { id: 'sport', name: 'myroot.category.sport' }, + { id: 'books', name: 'myroot.category.books' }, + { id: 'sport', name: 'myroot.category.sport' }, ]; ``` -You can render any item as disabled by setting its `disabled` property to `true`: +You can render any item as disabled by setting its `disabled` property to `true`: ```jsx const choices = [ @@ -1788,10 +2034,15 @@ const choices = [ { _id: 456, full_name: 'Jane Austen', sex: 'F' }, { _id: 1, full_name: 'System Administrator', sex: 'F', disabled: true }, ]; - +; ``` -You can use a custom field name by setting the `disableValue` prop: +You can use a custom field name by setting the `disableValue` prop: ```jsx const choices = [ @@ -1799,15 +2050,23 @@ const choices = [ { _id: 456, full_name: 'Jane Austen', sex: 'F' }, { _id: 987, full_name: 'Jack Harden', sex: 'M', not_available: true }, ]; - +; ``` Lastly, use the `options` attribute if you want to override any of the ` - + + ``` @@ -2640,9 +2927,19 @@ const ItemEdit = (props) => ( ```jsx const LatLongInput = () => ( - +   - + ); ``` @@ -2659,9 +2956,19 @@ import { Labeled } from 'react-admin'; const LatLngInput = () => ( - +   - + ); @@ -2673,8 +2980,8 @@ Now the component will render with a label: ```html - - + + ``` @@ -2690,7 +2997,7 @@ import { useField } from 'react-final-form'; const BoundedTextField = ({ name, label }) => { const { input: { onChange }, - meta: { touched, error } + meta: { touched, error }, } = useField(name); return ( { const { input: { name, onChange, ...rest }, meta: { touched, error }, - isRequired + isRequired, } = useInput(props); return ( @@ -2765,13 +3072,23 @@ const BoundedTextField = props => { ); }; const LatLngInput = props => { - const {source, ...rest} = props; + const { source, ...rest } = props; return ( - +   - + ); }; @@ -2788,14 +3105,11 @@ import { useInput } from 'react-admin'; const SexInput = props => { const { input, - meta: { touched, error } + meta: { touched, error }, } = useInput(props); return ( - Male Female @@ -2807,16 +3121,20 @@ export default SexInput; **Tip**: `useInput` accepts all arguments that you can pass to `useField`. That means that components using `useInput` accept props like [format](https://final-form.org/docs/react-final-form/types/FieldProps#format) and [parse](https://final-form.org/docs/react-final-form/types/FieldProps#parse), to convert values from the form to the input, and vice-versa: ```jsx -const parse = value => {/* ... */}; -const format = value => {/* ... */}; +const parse = value => { + /* ... */ +}; +const format = value => { + /* ... */ +}; const PersonEdit = props => ( formValue === 0 ? 'M' : 'F'} - parse={inputValue => inputValue === 'M' ? 0 : 1} + format={formValue => (formValue === 0 ? 'M' : 'F')} + parse={inputValue => (inputValue === 'M' ? 0 : 1)} /> @@ -2827,10 +3145,11 @@ const PersonEdit = props => ( You can find components for react-admin in third-party repositories. -- [vascofg/react-admin-color-input](https://github.com/vascofg/react-admin-color-input): a color input using [React Color](https://casesandberg.github.io/react-color/), a collection of color pickers. -- [vascofg/react-admin-date-inputs](https://github.com/vascofg/react-admin-date-inputs): a collection of Date Inputs, based on [material-ui-pickers](https://material-ui-pickers.firebaseapp.com/) -- [MrHertal/react-admin-json-view](https://github.com/MrHertal/react-admin-json-view): JSON field and input for react-admin. -- [@bb-tech/ra-components](https://github.com/bigbasket/ra-components): `JsonInput` which allows only valid JSON as input, `JsonField` to view JSON properly on show card and `TrimField` to trim the fields while showing in `Datagrid` in `List` component. -- [@react-page/react-admin](https://react-page.github.io/docs/#/integration-react-admin): ReactPage is a rich content editor and can comes with a ready-to-use React-admin input component. [check out the demo](https://react-page.github.io/examples/reactadmin) +- [marmelab/ra-richtext-tiptap](https://github.com/marmelab/ra-richtext-tiptap): a rich text input based on [Tiptap](https://www.tiptap.dev/) +- [vascofg/react-admin-color-input](https://github.com/vascofg/react-admin-color-input): a color input using [React Color](https://casesandberg.github.io/react-color/), a collection of color pickers. +- [vascofg/react-admin-date-inputs](https://github.com/vascofg/react-admin-date-inputs): a collection of Date Inputs, based on [material-ui-pickers](https://material-ui-pickers.firebaseapp.com/) +- [MrHertal/react-admin-json-view](https://github.com/MrHertal/react-admin-json-view): JSON field and input for react-admin. +- [@bb-tech/ra-components](https://github.com/bigbasket/ra-components): `JsonInput` which allows only valid JSON as input, `JsonField` to view JSON properly on show card and `TrimField` to trim the fields while showing in `Datagrid` in `List` component. +- [@react-page/react-admin](https://react-page.github.io/docs/#/integration-react-admin): ReactPage is a rich content editor and can comes with a ready-to-use React-admin input component. [check out the demo](https://react-page.github.io/examples/reactadmin) -- **DEPRECATED V3** [LoicMahieu/aor-tinymce-input](https://github.com/LoicMahieu/aor-tinymce-input): a TinyMCE component, useful for editing HTML +- **DEPRECATED V3** [LoicMahieu/aor-tinymce-input](https://github.com/LoicMahieu/aor-tinymce-input): a TinyMCE component, useful for editing HTML