Use the Datagrid component with custom queries - react-admin - react-admin

Receive below errors, when using Datagrid component with custom queries. Below code works with react-admin ver 3.3.1, whereas it doesn't work with ver 3.8.1
TypeError: Cannot read property 'includes' of undefined
Browser's console info: List components must be used inside a <ListContext.Provider>. Relying on props rather than context to get List data and callbacks is deprecated and won't be supported in the next major version of react-admin.
Refer: https://marmelab.com/react-admin/List.html
#Tip: You can use the Datagrid component with custom queries:
import keyBy from 'lodash/keyBy'
import { useQuery, Datagrid, TextField, Pagination, Loading } from 'react-admin'
const CustomList = () => {
const [page, setPage] = useState(1);
const perPage = 50;
const { data, total, loading, error } = useQuery({
type: 'GET_LIST',
resource: 'posts',
payload: {
pagination: { page, perPage },
sort: { field: 'id', order: 'ASC' },
filter: {},
}
});
if (loading) {
return <Loading />
}
if (error) {
return <p>ERROR: {error}</p>
}
return (
<>
<Datagrid
data={keyBy(data, 'id')}
ids={data.map(({ id }) => id)}
currentSort={{ field: 'id', order: 'ASC' }}
basePath="/posts" // required only if you set use "rowClick"
rowClick="edit"
>
<TextField source="id" />
<TextField source="name" />
</Datagrid>
<Pagination
page={page}
perPage={perPage}
setPage={setPage}
total={total}
/>
</>
)
} ```
Please help!

Since react-admin 3.7, <Datagrid> and <Pagination> read data from a ListContext, instead of expecting the data to be injected by props. See for instance the updated <Datagrid> docs at https://marmelab.com/react-admin/List.html#the-datagrid-component.
Your code will work if you wrap it in a <ListContextProvider>:
import React, { useState } from 'react';
import keyBy from 'lodash/keyBy'
import { useQuery, Datagrid, TextField, Pagination, Loading, ListContextProvider } from 'react-admin'
export const CustomList = () => {
const [page, setPage] = useState(1);
const perPage = 50;
const { data, total, loading, error } = useQuery({
type: 'GET_LIST',
resource: 'posts',
payload: {
pagination: { page, perPage },
sort: { field: 'id', order: 'ASC' },
filter: {},
}
});
if (loading) {
return <Loading />
}
if (error) {
return <p>ERROR: {error}</p>
}
return (
<ListContextProvider value={{
data: keyBy(data, 'id'),
ids: data.map(({ id }) => id),
total,
page,
perPage,
setPage,
currentSort: { field: 'id', order: 'ASC' },
basePath: "/posts",
resource: 'posts',
selectedIds: []
}}>
<Datagrid rowClick="edit">
<TextField source="id" />
<TextField source="name" />
</Datagrid>
<Pagination />
</ListContextProvider >
)
}

<ReferenceManyField>, as well as other relationship-related components, also implement a ListContext. That means you can use a <Datagrid> of a <Pagination> inside this component.
https://marmelab.com/react-admin/List.html#uselistcontext
Your code should look like this:
import React, { useState } from 'react';
import keyBy from 'lodash/keyBy'
import { useQuery, Datagrid, TextField, Pagination, Loading, ListContextProvider } from 'react-admin'
export const CustomList = () => {
return (
<ReferenceManyField reference="Your resource for pull the data" target="linked field">
<Datagrid rowClick="edit">
<TextField source="id" />
<TextField source="name" />
</Datagrid>
</ReferenceManyField>
)
}

Related

Autocompletearrayinput TypeError: Polyglot.transformPhrase expects argument #1 to be string

Hello stack overflow I was wondering if its possible to pre-populate with default values upon rendering. I'm also getting an error when using the from react admin. Here's my how i'm using my .
return (
<Edit {...props}>
<SimpleForm>
<TextInput source="audience_name" />
<ReferenceInput label="entity_ids" source="entity_ids" reference="posts">
<EntityInput setEntityLabel={onChangeLabel} onSelectEntity={addEntity} entityNames={entityNames} />
</ReferenceInput>
<br />
<AutocompleteArrayInput
source="tags"
shouldRenderSuggestions={(val) => {
console.log(val);
return val.trim().length > 0;
}}
choices={[
{ id: 'programming', name: 'Programming' },
{ id: 'lifestyle', name: 'Lifestyle' },
{ id: 'photography', name: 'Photography' },
]}
/>
</SimpleForm>
</Edit>
);
};
Try to add the props translateChoice={false}, like:
<AutocompleteInput source="first_name" choices={choices} translateChoice={false}/>
You can try recreating the i18nProvider, like this:
import polyglotI18nProvider from "ra-i18n-polyglot"; // Install this package
import engMessages from "ra-language-english"; // Install this package
const App = () => {
const i18nProvider = polyglotI18nProvider((locale) => engMessages, "en", {
allowMissing: true,
onMissingKey: (key, _, __) => key,
});
return (
<Admin
...
i18nProvider={i18nProvider}
>
)
}

Hi can anyone explain how to test formik with jest in react native

Hi I created form builder with formik for react native. I'm using jest for testing, but when I test onSubmit is not calling can anyone please explain how to test.
function FormBuilder({data, onSubmit, initialValues, navigation}) {
const formik = useFormik({
enableReinitialize: true,
initialValues: initialValues,
onSubmit: data => {
onSubmit(data);
},
});
return (
<View>
{data.map((item,index) => {
switch (item.type) {
case 'text':
return (
<TextBox
key={index}
onChangeText={formik.handleChange(item.name)}
onBlur={formik.handleBlur(item.name)}
value={formik.values[item.name]}
label={item.name}
touched={formik.touched}
errors={formik.errors}
required={
!!item.validators &&
item.validators.find(valid => valid.type === 'required')
}
{...item}
/>
);
case 'button':
return (
<CustomButton key={index} testID={item.testID} title=
{item.name} onPress={formik.handleSubmit} />
);
}
})}
</View>
)
}
and I call this component like this in my screen. Can anyone explain how can we write test Integration test for this
<FormBuilder
initialValues={initialValues}
data={[
{
type: 'text',
name: 'whatsAppNumber',
testID: 'input',
},
{type: 'button', name: 'login', testID: 'button'},
]}
onSubmit={submitData}
/>
you can test your code by following method, please use msw for creating dummy server for api call.
import {fireEvent, render,} from '#testing-library/react-native';
describe('Login Screen', () => {
it('should validate form', async () => {
const {getByTestId} = render(<Login />);
const numberField = getByTestId('input');
const button = getByTestId('button');
expect(numberField).toBeDefined();
expect(button).toBeDefined();
fireEvent.changeText(numberField, '9876543215');
fireEvent.press(button)
await waitFor(() =>{
//expect your function to be called
})
});
});

React Admin: how to pass state to transform

I have a component for creating media which uploads the media first to S3, then puts the returned values into the component's state:
import { Create, ReferenceInput, SelectInput, SimpleForm, TextInput } from 'react-admin';
import { Field } from 'react-final-form';
import React, { useState } from 'react';
import { ImageHandler } from './ImageHandler';
import { BeatLoader } from 'react-spinners';
export const MediaCreate = props => {
const [image, setImage] = useState(null);
const [isUploading, setIsUploading] = useState(false);
console.log(image) // <-- this contains the image object after uploading
const transform = data => {
console.log(image) // <-- this is NULL after clicking submit
return {
...data,
key: image.key,
mime_type: image.mime
}
};
return (
<Create {...props} transform={transform}>
<SimpleForm>
<SelectInput source="collection" label="Type" choices={[
{ id: 'gallery', name: 'Gallery' },
{ id: 'attachment', name: 'Attachment' },
]}/>
<ReferenceInput label="Asset" source="asset_id" reference="assets">
<SelectInput optionText="name"/>
</ReferenceInput>
<TextInput source={'name'} />
{isUploading &&
<div style={{ display: 'flex', alignItems: 'center' }}>
<BeatLoader
size={10}
color={"#123abc"}
loading={isUploading}
/> Uploading, please wait
</div>
}
<ImageHandler
isUploading={(isUploading) => setIsUploading(isUploading)}
onUploaded={(image) => setImage(image)}
/>
</SimpleForm>
</Create>
);
};
Why is image null despite containing the value after upload? How can I pass in my component state to the transform function?
This can be reached by useRef
export const MediaCreate = props => {
const [image, setImage] = useState(null);
const [isUploading, setIsUploading] = useState(false);
const someRef = useRef()
someRef.current = image
const transform = data => {
console.log(someRef.current) // <-- this will not be NULL
return {
...data,
key: someRef.current.key,
mime_type: someRef.current.mime
}
};

Prevent react-admin from redirect to first page after each update?

using react-admin i created an app with sample list with many pages then i ran it. if i opened another page in the list and chose to edit a row or to delete it, the task done but the list is redirected to the first page and this is not good for user experience. if the user want to review multiple rows and edit them this will oblige him to return to the page each time he made edit. i am not sure if this is a how to question or a bug or feature that should be posted in github. i tested it in multiple react-admin versions 3.6.0, 3.5.5, 3.0.0 and the same behavior appeared.
// in src/App.js
import * as React from "react";
import { Admin, Resource } from "react-admin";
import jsonServerProvider from "ra-data-json-server";
import CommentList from "./comments";
const dataProvider = jsonServerProvider("https://jsonplaceholder.typicode.com");
const App = () => (
<Admin dataProvider={dataProvider}>
<Resource name="comments" list={CommentList} />
</Admin>
);
export default App;
import * as React from "react";
import {
List,
Datagrid,
TextField,
ReferenceField,
EmailField
} from "react-admin";
import { Fragment } from "react";
import { BulkDeleteButton } from "react-admin";
import ResetViewsButton from "./ResetViewsButton";
const PostBulkActionButtons = props => (
<Fragment>
<ResetViewsButton label="Reset Views" {...props} />
{/* default bulk delete action */}
<BulkDeleteButton {...props} />
</Fragment>
);
const CommentList = props => (
<List {...props} bulkActionButtons={<PostBulkActionButtons />}>
<Datagrid rowClick="edit">
<ReferenceField source="postId" reference="posts">
<TextField source="id" />
</ReferenceField>
<TextField source="id" />
<TextField source="name" />
<EmailField source="email" />
<TextField source="body" />
</Datagrid>
</List>
);
export default CommentList;
import * as React from "react";
import { Button, useUpdateMany, useNotify, useUnselectAll } from "react-admin";
import { VisibilityOff } from "#material-ui/icons";
const ResetViewsButton = props => {
const notify = useNotify();
const unselectAll = useUnselectAll();
console.log(props.selectedIds);
console.log(props.basePath);
const [updateMany, { loading }] = useUpdateMany(
"comments",
props.selectedIds,
{ emails: "" },
{
onSuccess: () => {
notify("comments updated");
unselectAll("comments");
},
onFailure: error => notify("Error: comments not updated", "warning")
}
);
return (
<Button
label="simple.action.resetViews"
disabled={loading}
onClick={updateMany}
>
<VisibilityOff />
</Button>
);
};
export default ResetViewsButton;
You can use "useRedirect" If I am not getting you wrong. You want to redirect after edit info.
import { useRedirect } from 'react-admin';
const ResetViewsButton = props => {
const notify = useNotify();
const redirectTo = useRedirect();
const unselectAll = useUnselectAll();
console.log(props.selectedIds);
console.log(props.basePath);
const [updateMany, { loading }] = useUpdateMany(
"comments",
props.selectedIds,
{ emails: "" },
{
onSuccess: () => {
notify("comments updated");
unselectAll("comments");
redirectTo('/url');
},
onFailure: error => notify("Error: comments not updated", "warning")
}
);
return (
<Button
label="simple.action.resetViews"
disabled={loading}
onClick={updateMany}
>
<VisibilityOff />
</Button>
);
};
export default ResetViewsButton;
it was a bug. wait version 3.6.1

How can I get react-select to integrate properly with react-final-form

I'm using react-final-form to send along some basic user data. First name, last name, email and a dropdown value with a reason why they're contacting me. I tried the following (the custom input section on react-final-form's docs):
<Field name="myField">
{props => (
<div>
<Select
options={colourOptions}
className="basic-multi-select"
classNamePrefix="select"
onChange={props.input.onChange}
/>
</div>
)}
</Field>
My guess is maybe there's some collision with the onChange prop? Not sure, but the information in the dropdown is not being picked up by react-final-form and pass along to the <form> on submit.
There is an example on how to use react-select with react-final-form: https://codesandbox.io/s/40mr0v2r87.
You should add an adapter:
const ReactSelectAdapter = ({ input, ...rest }) => (
<Select {...input} {...rest} />
)
And then you can use it as a component:
<Field
name="state"
component={ReactSelectAdapter}
options={states}
/>
Here my approach. I decided problem with initial values and value which will sent to server. Just like:
{selectFieldId: "value"}
import React from "react";
import { FieldRenderProps } from "react-final-form";
import Select, { ValueType } from "react-select";
type Option = {
label: string;
value: string;
};
export const CustomSelect = ({ input, options, ...rest }: FieldRenderProps<string, HTMLElement>) => {
const handleChange = (option: ValueType<Option, false>) => {
input.onChange(option?.value);
};
return (
<Select
{...input}
{...rest}
onChange={handleChange}
options={options}
value={options ? options.find((option: Option) => option.value === input.value) : ""}
/>
);
};
export default CustomSelect;
in a simple way I have done it like this:
const locationOptions = [
{ value: 1, label: "ADEN" },
{ value: 2, label: "MUKALLA" },
{ value: 3, label: "TAIZ" },
];
<Field name="location_id">
{({ input }) => (
<Select
options={locationOptions}
placeholder="Select Location"
{...input}
/>
)}
</Field>
the on submit method:
const onSubmit = (data) => {
console.log(data);
};