From e52840744ac1cf87b4308dfcf7eeec8000c54363 Mon Sep 17 00:00:00 2001 From: Erik VanderWerf Date: Tue, 31 Jan 2023 00:28:28 -0800 Subject: [PATCH 1/4] Touching up Tutorial.md with improvements to TypeScript examples. --- docs/Tutorial.md | 22 ++++++++++++---------- examples/tutorial/src/authProvider.ts | 4 +++- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/docs/Tutorial.md b/docs/Tutorial.md index 08daf61707d..73a07127ccc 100644 --- a/docs/Tutorial.md +++ b/docs/Tutorial.md @@ -459,8 +459,8 @@ import { UserList } from "./users"; const App = () => ( -+ - + ++ ); @@ -755,7 +755,7 @@ export const PostEdit = () => ( [![Post Edit Title](./img/tutorial_post_title.png)](./img/tutorial_post_title.png) -This component uses the same `useRecordContext` hook as the custom `` commponent described earlier. +This component uses the same `useRecordContext` hook as the custom `` component described earlier. As users can access the post editing page directly by its url, the `` component may render *without a record* while the `` component is fetching it. That's why you must always check that the `record` returned by `useRecordContext` is defined before using it - as in `PostTitle` above. @@ -845,9 +845,11 @@ For this tutorial, since there is no public authentication API, we can use a fak The `authProvider` must expose 5 methods, each returning a `Promise`: -```jsx +```js // in src/authProvider.ts -export const authProvider = { +import {AuthProvider} from "react-admin"; + +export const authProvider: AuthProvider = { // called when the user attempts to log in login: ({ username }) => { localStorage.setItem("username", username); @@ -924,13 +926,13 @@ React-admin calls the Data Provider with one method for each of the actions of t The code for a Data Provider for the `my.api.url` API is as follows: ```js -import { fetchUtils } from "react-admin"; +import { DataProvider, fetchUtils } from "react-admin"; import { stringify } from "query-string"; const apiUrl = 'https://my.api.com/'; const httpClient = fetchUtils.fetchJson; -export const dataProvider= { +export const dataProvider: DataProvider = { getList: (resource, params) => { const { page, perPage } = params.pagination; const { field, order } = params.sort; @@ -943,7 +945,7 @@ export const dataProvider= { return httpClient(url).then(({ headers, json }) => ({ data: json, - total: parseInt(headers.get('content-range').split('/').pop(), 10), + total: parseInt((headers.get('content-range') || "0").split('/').pop()!, 10), })); }, @@ -975,7 +977,7 @@ export const dataProvider= { return httpClient(url).then(({ headers, json }) => ({ data: json, - total: parseInt(headers.get('content-range').split('/').pop(), 10), + total: parseInt((headers.get('content-range') || "0").split('/').pop()!, 10), })); }, @@ -1040,4 +1042,4 @@ React-admin was built with customization in mind. You can replace any react-admi Now that you've completed the tutorial, continue your journey with [the Features chapter](./Features.md), which lists all the features of react-admin. -**Tip**: To help you close the gap between theoritical knowledge and practical experience, take advantage of the react-admin [Demos](./Demos.md). They are great examples of how to use react-admin in a real world application. They also show the best practices for going beyond simple CRUD apps. +**Tip**: To help you close the gap between theoretical knowledge and practical experience, take advantage of the react-admin [Demos](./Demos.md). They are great examples of how to use react-admin in a real world application. They also show the best practices for going beyond simple CRUD apps. diff --git a/examples/tutorial/src/authProvider.ts b/examples/tutorial/src/authProvider.ts index 8d632129589..1b24e7196a0 100644 --- a/examples/tutorial/src/authProvider.ts +++ b/examples/tutorial/src/authProvider.ts @@ -1,4 +1,6 @@ -export const authProvider = { +import { AuthProvider } from 'react-admin'; + +export const authProvider: AuthProvider = { // called when the user attempts to log in login: ({ username }: { username: string }) => { localStorage.setItem('username', username); From 9ad9098010fc4b51142fdae758f85bf1f494c833 Mon Sep 17 00:00:00 2001 From: Erik VanderWerf Date: Tue, 31 Jan 2023 00:32:10 -0800 Subject: [PATCH 2/4] Fixing TS-only syntax. --- docs/Tutorial.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/Tutorial.md b/docs/Tutorial.md index 73a07127ccc..ddfb0f5e7c9 100644 --- a/docs/Tutorial.md +++ b/docs/Tutorial.md @@ -945,7 +945,7 @@ export const dataProvider: DataProvider = { return httpClient(url).then(({ headers, json }) => ({ data: json, - total: parseInt((headers.get('content-range') || "0").split('/').pop()!, 10), + total: parseInt((headers.get('content-range') || "0").split('/').pop() || 0, 10), })); }, @@ -977,7 +977,7 @@ export const dataProvider: DataProvider = { return httpClient(url).then(({ headers, json }) => ({ data: json, - total: parseInt((headers.get('content-range') || "0").split('/').pop()!, 10), + total: parseInt((headers.get('content-range') || "0").split('/').pop() || 0, 10), })); }, From a04c07b762b59e02e49da3b85b7a11bf9b3b5e08 Mon Sep 17 00:00:00 2001 From: Erik VanderWerf Date: Fri, 3 Feb 2023 11:48:31 -0800 Subject: [PATCH 3/4] Tutorial examples now use .js instead of .ts. Noted required changes for TS in comments. --- docs/Tutorial.md | 70 +++++++++++++++++++++++++----------------------- 1 file changed, 36 insertions(+), 34 deletions(-) diff --git a/docs/Tutorial.md b/docs/Tutorial.md index ddfb0f5e7c9..d412f804857 100644 --- a/docs/Tutorial.md +++ b/docs/Tutorial.md @@ -69,7 +69,7 @@ JSONPlaceholder provides endpoints for users, posts, and comments. The admin we' Bootstrap the admin app by replacing the `src/App.tsx` by the following code: ```jsx -// in src/App.tsx +// in src/App.jsx import { Admin } from "react-admin"; import jsonServerProvider from "ra-data-json-server"; @@ -126,7 +126,7 @@ We'll start by adding a list of users. The `` component expects one or more `` child components. Each resource maps a name to an endpoint in the API. Edit the `App.tsx` file to add a resource named `users`: ```diff -// in src/App.tsx +// in src/App.jsx -import { Admin } from "react-admin"; +import { Admin, Resource, ListGuesser } from "react-admin"; import jsonServerProvider from "ra-data-json-server"; @@ -162,7 +162,7 @@ The `` component is not meant to be used in production - it's just Let's copy this code, and create a new `UserList` component, in a new file named `users.tsx`: ```jsx -// in src/users.tsx +// in src/users.jsx import { List, Datagrid, TextField, EmailField } from "react-admin"; export const UserList = () => ( @@ -184,7 +184,7 @@ export const UserList = () => ( Then, edit the `App.tsx` file to use this new component instead of `ListGuesser`: ```diff -// in src/App.tsx +// in src/App.jsx -import { Admin, Resource, ListGuesser } from "react-admin"; +import { Admin, Resource } from "react-admin"; import jsonServerProvider from "ra-data-json-server"; @@ -238,7 +238,7 @@ But in most frameworks, "simple" means "limited", and it's hard to go beyond bas This means we can compose `` with another component - for instance ``: ```jsx -// in src/users.tsx +// in src/users.jsx import { List, SimpleList } from "react-admin"; export const UserList = () => ( @@ -271,7 +271,7 @@ But on desktop, `` takes too much space for a low information densit To do so, we'll use [the `useMediaQuery` hook](https://mui.com/material-ui/react-use-media-query/#main-content) from MUI: ```jsx -// in src/users.tsx +// in src/users.jsx import { useMediaQuery } from "@mui/material"; import { List, SimpleList, Datagrid, TextField, EmailField } from "react-admin"; @@ -315,7 +315,7 @@ Let's get back to ``. It reads the data fetched by ``, then rend `` created one column for every field in the response. That's a bit too much for a usable grid, so let's remove a couple `` from the Datagrid and see the effect: ```diff -// in src/users.tsx +// in src/users.jsx @@ -339,7 +339,7 @@ You've just met the `` and the `` components. React-admin For instance, the `website` field looks like a URL. Instead of displaying it as text, why not display it using a clickable link? That's exactly what the `` does: ```diff -// in src/users.tsx +// in src/users.jsx -import { List, SimpleList, Datagrid, TextField, EmailField } from "react-admin"; +import { List, SimpleList, Datagrid, TextField, EmailField, UrlField } from "react-admin"; // ... @@ -365,7 +365,7 @@ In react-admin, fields are just React components. When rendered, they grab the ` That means that you can do the same to write a custom Field. For instance, here is a simplified version of the ``: ```jsx -// in src/MyUrlField.tsx +// in src/MyUrlField.jsx import { useRecordContext } from "react-admin"; const MyUrlField = ({ source }) => { @@ -382,7 +382,7 @@ For each row, `` creates a `RecordContext` and stores the current reco You can use the `` component in ``, instead of react-admin's `` component, and it will work just the same. ```diff -// in src/users.tsx +// in src/users.jsx -import { List, SimpleList, Datagrid, TextField, EmailField, UrlField } from "react-admin"; +import { List, SimpleList, Datagrid, TextField, EmailField } from "react-admin"; +import MyUrlField from './MyUrlField'; @@ -408,7 +408,7 @@ React-admin relies on [MUI](https://mui.com/), a set of React components modeled {% raw %} ```jsx -// in src/MyUrlField.tsx +// in src/MyUrlField.jsx import { useRecordContext } from "react-admin"; import { Link } from "@mui/material"; import LaunchIcon from "@mui/icons-material/Launch"; @@ -451,7 +451,7 @@ In JSONPlaceholder, each `post` record includes a `userId` field, which points t React-admin knows how to take advantage of these foreign keys to fetch references. Let's see how the `ListGuesser` manages them by creating a new `` for the `/posts` API endpoint: ```diff -// in src/App.tsx +// in src/App.jsx -import { Admin, Resource } from "react-admin"; +import { Admin, Resource, ListGuesser } from "react-admin"; import jsonServerProvider from "ra-data-json-server"; @@ -472,7 +472,7 @@ export default App; The `ListGuesser` suggests using a `` for the `userId` field. Let's play with this new field by creating the `PostList` component based on the code dumped by the guesser: ```jsx -// in src/posts.tsx +// in src/posts.jsx import { List, Datagrid, TextField, ReferenceField } from "react-admin"; export const PostList = () => ( @@ -488,7 +488,7 @@ export const PostList = () => ( ``` ```diff -// in src/App.tsx +// in src/App.jsx -import { Admin, Resource, ListGuesser } from "react-admin"; +import { Admin, Resource } from "react-admin"; +import { PostList } from "./posts"; @@ -506,7 +506,7 @@ const App = () => ( When displaying the posts list, the app displays the `id` of the post author. This doesn't mean much - we should use the user `name` instead. For that purpose, set the `recordRepresentation` prop of the "users" Resource: ```diff -// in src/App.tsx +// in src/App.jsx const App = () => ( @@ -527,7 +527,7 @@ The `` component fetches the reference data, creates a `RecordCo To finish the post list, place the post `id` field as first column, and remove the `body` field. From a UX point of view, fields containing large chunks of text should not appear in a Datagrid, only in detail views. Also, to make the Edit action stand out, let's replace the `rowClick` action by an explicit action button: ```diff -// in src/posts.tsx +// in src/posts.jsx -import { List, Datagrid, TextField, ReferenceField } from "react-admin"; +import { List, Datagrid, TextField, ReferenceField, EditButton } from "react-admin"; @@ -553,7 +553,7 @@ export const PostList = () => ( An admin interface isn't just about displaying remote data, it should also allow editing records. React-admin provides an `` component for that purpose ; let's use the `` to help bootstrap it. ```diff -// in src/App.tsx +// in src/App.jsx -import { Admin, Resource } from "react-admin"; +import { Admin, Resource, EditGuesser } from "react-admin"; import { PostList } from "./posts"; @@ -575,7 +575,7 @@ Users can display the edit page just by clicking on the Edit button. The form is Copy the `` code dumped by the guesser in the console to the `posts.tsx` file so that you can customize the view: ```jsx -// in src/posts.tsx +// in src/posts.jsx import { List, Datagrid, @@ -607,7 +607,7 @@ export const PostEdit = () => ( Use that component as the `edit` prop of the "posts" Resource instead of the guesser: ```diff -// in src/App.tsx +// in src/App.jsx -import { Admin, Resource, EditGuesser } from "react-admin"; +import { Admin, Resource } from "react-admin"; import jsonServerProvider from "ra-data-json-server"; @@ -629,7 +629,7 @@ const App = () => ( You can now adjust the `` component to disable the edition of the primary key (`id`), place it first, and use a textarea for the `body` field, as follows: ```diff -// in src/posts.tsx +// in src/posts.jsx export const PostEdit = () => ( @@ -653,7 +653,7 @@ The `` takes the same props as the `` (used earl Let's allow users to create posts, too. Copy the `` component into a ``, and replace `` by ``: ```diff -// in src/posts.tsx +// in src/posts.jsx import { List, Datagrid, @@ -691,7 +691,7 @@ export const PostEdit = () => ( To use the new `` components in the posts resource, just add it as `create` attribute in the `` component: ```diff -// in src/App.tsx +// in src/App.jsx import { Admin, Resource } from "react-admin"; -import { PostList, PostEdit } from "./posts"; +import { PostList, PostEdit, PostCreate } from "./posts"; @@ -735,7 +735,7 @@ The post editing page has a slight problem: it uses the post id as main title (t Let's customize the view title with a custom title component: ```diff -// in src/posts.tsx +// in src/posts.jsx +import { useRecordContext} from "react-admin"; // ... @@ -766,7 +766,7 @@ Let's get back to the post list for a minute. It offers sorting and pagination, React-admin can use Input components to create a multi-criteria search engine in the list view. Pass an array of such Input components to the List `filters` prop to enable filtering: ```jsx -// in src/posts.tsx +// in src/posts.jsx const postFilters = [ , , @@ -792,7 +792,7 @@ Filters are "search-as-you-type", meaning that when the user enters new values i The sidebar menu shows the same icon for both posts and users. Customizing the menu icon is just a matter of passing an `icon` attribute to each ``: ```jsx -// in src/App.tsx +// in src/App.jsx import PostIcon from "@mui/icons-material/Book"; import UserIcon from "@mui/icons-material/Group"; @@ -811,7 +811,7 @@ const App = () => ( By default, react-admin displays the list page of the first `Resource` element as home page. If you want to display a custom component instead, pass it in the `dashboard` prop of the `` component. ```jsx -// in src/Dashboard.tsx +// in src/Dashboard.jsx import { Card, CardContent, CardHeader } from "@mui/material"; export const Dashboard = () => ( @@ -823,7 +823,7 @@ export const Dashboard = () => ( ``` ```jsx -// in src/App.tsx +// in src/App.jsx import { Dashboard } from './Dashboard'; const App = () => ( @@ -846,10 +846,10 @@ For this tutorial, since there is no public authentication API, we can use a fak The `authProvider` must expose 5 methods, each returning a `Promise`: ```js -// in src/authProvider.ts -import {AuthProvider} from "react-admin"; +// in src/authProvider.js -export const authProvider: AuthProvider = { +// TypeScript must reference the type `: AuthProvider` +export const authProvider = { // called when the user attempts to log in login: ({ username }) => { localStorage.setItem("username", username); @@ -885,7 +885,7 @@ export const authProvider: AuthProvider = { To enable this authentication strategy, pass the `authProvider` to the `` component: ```jsx -// in src/App.tsx +// in src/App.jsx import { Dashboard } from './Dashboard'; import { authProvider } from './authProvider'; @@ -926,13 +926,15 @@ React-admin calls the Data Provider with one method for each of the actions of t The code for a Data Provider for the `my.api.url` API is as follows: ```js -import { DataProvider, fetchUtils } from "react-admin"; +// in src/dataProvider.js +import { fetchUtils } from "react-admin"; import { stringify } from "query-string"; const apiUrl = 'https://my.api.com/'; const httpClient = fetchUtils.fetchJson; -export const dataProvider: DataProvider = { +// TypeScript must reference the type `: DataProvider` +export const dataProvider = { getList: (resource, params) => { const { page, perPage } = params.pagination; const { field, order } = params.sort; @@ -1026,7 +1028,7 @@ export const dataProvider: DataProvider = { Using this provider instead of the previous `jsonServerProvider` is just a matter of switching a function: ```jsx -// in src/app.tsx +// in src/app.jsx import { dataProvider } from './dataProvider'; const App = () => ( From 5b6eb335371263d92f5511c3c8d612698019a8c2 Mon Sep 17 00:00:00 2001 From: Erik VanderWerf Date: Thu, 16 Feb 2023 22:30:10 -0800 Subject: [PATCH 4/4] Snippets are written in JS, but with comments explaining TS usage, and reference TS project files. --- docs/Tutorial.md | 68 ++++++++++++++++++++++++------------------------ 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/docs/Tutorial.md b/docs/Tutorial.md index d412f804857..31565b98a9e 100644 --- a/docs/Tutorial.md +++ b/docs/Tutorial.md @@ -69,7 +69,7 @@ JSONPlaceholder provides endpoints for users, posts, and comments. The admin we' Bootstrap the admin app by replacing the `src/App.tsx` by the following code: ```jsx -// in src/App.jsx +// in src/App.tsx import { Admin } from "react-admin"; import jsonServerProvider from "ra-data-json-server"; @@ -87,7 +87,7 @@ That's enough for react-admin to render an empty app and confirm that the setup Also, you should change the default Vite CSS file to look like this: ```css -// in src/index.css +/* in src/index.css */ body { margin: 0; } @@ -126,7 +126,7 @@ We'll start by adding a list of users. The `` component expects one or more `` child components. Each resource maps a name to an endpoint in the API. Edit the `App.tsx` file to add a resource named `users`: ```diff -// in src/App.jsx +// in src/App.tsx -import { Admin } from "react-admin"; +import { Admin, Resource, ListGuesser } from "react-admin"; import jsonServerProvider from "ra-data-json-server"; @@ -162,7 +162,7 @@ The `` component is not meant to be used in production - it's just Let's copy this code, and create a new `UserList` component, in a new file named `users.tsx`: ```jsx -// in src/users.jsx +// in src/users.tsx import { List, Datagrid, TextField, EmailField } from "react-admin"; export const UserList = () => ( @@ -184,7 +184,7 @@ export const UserList = () => ( Then, edit the `App.tsx` file to use this new component instead of `ListGuesser`: ```diff -// in src/App.jsx +// in src/App.tsx -import { Admin, Resource, ListGuesser } from "react-admin"; +import { Admin, Resource } from "react-admin"; import jsonServerProvider from "ra-data-json-server"; @@ -238,7 +238,7 @@ But in most frameworks, "simple" means "limited", and it's hard to go beyond bas This means we can compose `` with another component - for instance ``: ```jsx -// in src/users.jsx +// in src/users.tsx import { List, SimpleList } from "react-admin"; export const UserList = () => ( @@ -271,7 +271,7 @@ But on desktop, `` takes too much space for a low information densit To do so, we'll use [the `useMediaQuery` hook](https://mui.com/material-ui/react-use-media-query/#main-content) from MUI: ```jsx -// in src/users.jsx +// in src/users.tsx import { useMediaQuery } from "@mui/material"; import { List, SimpleList, Datagrid, TextField, EmailField } from "react-admin"; @@ -315,7 +315,7 @@ Let's get back to ``. It reads the data fetched by ``, then rend `` created one column for every field in the response. That's a bit too much for a usable grid, so let's remove a couple `` from the Datagrid and see the effect: ```diff -// in src/users.jsx +// in src/users.tsx @@ -339,7 +339,7 @@ You've just met the `` and the `` components. React-admin For instance, the `website` field looks like a URL. Instead of displaying it as text, why not display it using a clickable link? That's exactly what the `` does: ```diff -// in src/users.jsx +// in src/users.tsx -import { List, SimpleList, Datagrid, TextField, EmailField } from "react-admin"; +import { List, SimpleList, Datagrid, TextField, EmailField, UrlField } from "react-admin"; // ... @@ -365,7 +365,7 @@ In react-admin, fields are just React components. When rendered, they grab the ` That means that you can do the same to write a custom Field. For instance, here is a simplified version of the ``: ```jsx -// in src/MyUrlField.jsx +// in src/MyUrlField.tsx import { useRecordContext } from "react-admin"; const MyUrlField = ({ source }) => { @@ -382,7 +382,7 @@ For each row, `` creates a `RecordContext` and stores the current reco You can use the `` component in ``, instead of react-admin's `` component, and it will work just the same. ```diff -// in src/users.jsx +// in src/users.tsx -import { List, SimpleList, Datagrid, TextField, EmailField, UrlField } from "react-admin"; +import { List, SimpleList, Datagrid, TextField, EmailField } from "react-admin"; +import MyUrlField from './MyUrlField'; @@ -408,7 +408,7 @@ React-admin relies on [MUI](https://mui.com/), a set of React components modeled {% raw %} ```jsx -// in src/MyUrlField.jsx +// in src/MyUrlField.tsx import { useRecordContext } from "react-admin"; import { Link } from "@mui/material"; import LaunchIcon from "@mui/icons-material/Launch"; @@ -451,7 +451,7 @@ In JSONPlaceholder, each `post` record includes a `userId` field, which points t React-admin knows how to take advantage of these foreign keys to fetch references. Let's see how the `ListGuesser` manages them by creating a new `` for the `/posts` API endpoint: ```diff -// in src/App.jsx +// in src/App.tsx -import { Admin, Resource } from "react-admin"; +import { Admin, Resource, ListGuesser } from "react-admin"; import jsonServerProvider from "ra-data-json-server"; @@ -472,7 +472,7 @@ export default App; The `ListGuesser` suggests using a `` for the `userId` field. Let's play with this new field by creating the `PostList` component based on the code dumped by the guesser: ```jsx -// in src/posts.jsx +// in src/posts.tsx import { List, Datagrid, TextField, ReferenceField } from "react-admin"; export const PostList = () => ( @@ -488,7 +488,7 @@ export const PostList = () => ( ``` ```diff -// in src/App.jsx +// in src/App.tsx -import { Admin, Resource, ListGuesser } from "react-admin"; +import { Admin, Resource } from "react-admin"; +import { PostList } from "./posts"; @@ -506,7 +506,7 @@ const App = () => ( When displaying the posts list, the app displays the `id` of the post author. This doesn't mean much - we should use the user `name` instead. For that purpose, set the `recordRepresentation` prop of the "users" Resource: ```diff -// in src/App.jsx +// in src/App.tsx const App = () => ( @@ -527,7 +527,7 @@ The `` component fetches the reference data, creates a `RecordCo To finish the post list, place the post `id` field as first column, and remove the `body` field. From a UX point of view, fields containing large chunks of text should not appear in a Datagrid, only in detail views. Also, to make the Edit action stand out, let's replace the `rowClick` action by an explicit action button: ```diff -// in src/posts.jsx +// in src/posts.tsx -import { List, Datagrid, TextField, ReferenceField } from "react-admin"; +import { List, Datagrid, TextField, ReferenceField, EditButton } from "react-admin"; @@ -553,7 +553,7 @@ export const PostList = () => ( An admin interface isn't just about displaying remote data, it should also allow editing records. React-admin provides an `` component for that purpose ; let's use the `` to help bootstrap it. ```diff -// in src/App.jsx +// in src/App.tsx -import { Admin, Resource } from "react-admin"; +import { Admin, Resource, EditGuesser } from "react-admin"; import { PostList } from "./posts"; @@ -575,7 +575,7 @@ Users can display the edit page just by clicking on the Edit button. The form is Copy the `` code dumped by the guesser in the console to the `posts.tsx` file so that you can customize the view: ```jsx -// in src/posts.jsx +// in src/posts.tsx import { List, Datagrid, @@ -607,7 +607,7 @@ export const PostEdit = () => ( Use that component as the `edit` prop of the "posts" Resource instead of the guesser: ```diff -// in src/App.jsx +// in src/App.tsx -import { Admin, Resource, EditGuesser } from "react-admin"; +import { Admin, Resource } from "react-admin"; import jsonServerProvider from "ra-data-json-server"; @@ -629,7 +629,7 @@ const App = () => ( You can now adjust the `` component to disable the edition of the primary key (`id`), place it first, and use a textarea for the `body` field, as follows: ```diff -// in src/posts.jsx +// in src/posts.tsx export const PostEdit = () => ( @@ -653,7 +653,7 @@ The `` takes the same props as the `` (used earl Let's allow users to create posts, too. Copy the `` component into a ``, and replace `` by ``: ```diff -// in src/posts.jsx +// in src/posts.tsx import { List, Datagrid, @@ -691,7 +691,7 @@ export const PostEdit = () => ( To use the new `` components in the posts resource, just add it as `create` attribute in the `` component: ```diff -// in src/App.jsx +// in src/App.tsx import { Admin, Resource } from "react-admin"; -import { PostList, PostEdit } from "./posts"; +import { PostList, PostEdit, PostCreate } from "./posts"; @@ -735,7 +735,7 @@ The post editing page has a slight problem: it uses the post id as main title (t Let's customize the view title with a custom title component: ```diff -// in src/posts.jsx +// in src/posts.tsx +import { useRecordContext} from "react-admin"; // ... @@ -766,7 +766,7 @@ Let's get back to the post list for a minute. It offers sorting and pagination, React-admin can use Input components to create a multi-criteria search engine in the list view. Pass an array of such Input components to the List `filters` prop to enable filtering: ```jsx -// in src/posts.jsx +// in src/posts.tsx const postFilters = [ , , @@ -792,7 +792,7 @@ Filters are "search-as-you-type", meaning that when the user enters new values i The sidebar menu shows the same icon for both posts and users. Customizing the menu icon is just a matter of passing an `icon` attribute to each ``: ```jsx -// in src/App.jsx +// in src/App.tsx import PostIcon from "@mui/icons-material/Book"; import UserIcon from "@mui/icons-material/Group"; @@ -811,7 +811,7 @@ const App = () => ( By default, react-admin displays the list page of the first `Resource` element as home page. If you want to display a custom component instead, pass it in the `dashboard` prop of the `` component. ```jsx -// in src/Dashboard.jsx +// in src/Dashboard.tsx import { Card, CardContent, CardHeader } from "@mui/material"; export const Dashboard = () => ( @@ -823,7 +823,7 @@ export const Dashboard = () => ( ``` ```jsx -// in src/App.jsx +// in src/App.tsx import { Dashboard } from './Dashboard'; const App = () => ( @@ -846,9 +846,9 @@ For this tutorial, since there is no public authentication API, we can use a fak The `authProvider` must expose 5 methods, each returning a `Promise`: ```js -// in src/authProvider.js +// in src/authProvider.ts -// TypeScript must reference the type `: AuthProvider` +// TypeScript users must reference the type: `AuthProvider` export const authProvider = { // called when the user attempts to log in login: ({ username }) => { @@ -885,7 +885,7 @@ export const authProvider = { To enable this authentication strategy, pass the `authProvider` to the `` component: ```jsx -// in src/App.jsx +// in src/App.tsx import { Dashboard } from './Dashboard'; import { authProvider } from './authProvider'; @@ -926,14 +926,14 @@ React-admin calls the Data Provider with one method for each of the actions of t The code for a Data Provider for the `my.api.url` API is as follows: ```js -// in src/dataProvider.js +// in src/dataProvider.ts import { fetchUtils } from "react-admin"; import { stringify } from "query-string"; const apiUrl = 'https://my.api.com/'; const httpClient = fetchUtils.fetchJson; -// TypeScript must reference the type `: DataProvider` +// TypeScript users must reference the type `DataProvider` export const dataProvider = { getList: (resource, params) => { const { page, perPage } = params.pagination; @@ -1028,7 +1028,7 @@ export const dataProvider = { Using this provider instead of the previous `jsonServerProvider` is just a matter of switching a function: ```jsx -// in src/app.jsx +// in src/app.tsx import { dataProvider } from './dataProvider'; const App = () => (