FormDataConsumer Within Filter - react-admin

I am using react-admin 3.6.2 and I am wondering, can the <FormDataConsumer /> component be used within a <Filter /> component? If so, could you please provide an example? If not, how else may I hide and reset one filter based on the use of another filter?
EDIT: I have tried using the FormDataConsumer within a Filter and I could not get it to work. Using dev tools in the browser, it never hits my break point at the if-statement. Code below:
<Filter>
<OtherInputsAsFiltersHere omitForBrevity />
<FormDataConsumer>
{({ formData, ...rest }) => {
if (!check.assigned(formData.sample_code))
return (
<DateInput
source="modified_on_gte"
label="Modified after"
{...rest}
/>
);
}}
</FormDataConsumer>
</Filter>

Related

Custom button bar in React-admin v4.0

I am trying to customize the user management page using <Admin> and <Resource> and am using React-Admin v.4.0. I need to add a bar with tabs below displayed resources (4 categories of users).
Whenever a tab is selected - "Not found" error is displayed error when selecting required tab) Although the name in the sidebar changes to the selected value and after the sidebar is clicked the whole dashboard is re-rendered to display the requested category of users, like here: page after the sidebar is clicked
I am trying to figure out if this is a React render related issue or something is missing in my react-admin part of the code. Without the custom bar and with a default appBar the page is working correctly.
My code looks like this:
import {UserList} from 'components/UserList';
const MyLayout_1 = ({ children }) => {
return (
<Layout appBar={ () => <></>} >
{children}
<h2>{heading}</h2>
<MyTabBar />
</Layout>
);
};
return (
<Admin dataProvider={dataProvider} layout={MyLayout_1}>
{selectedTab === 'users' && (
<Resource name="users" list={UserList} />
)}
{selectedTab === 'approved' && (
<Resource name="approved" list={UserList} />
)}
{selectedTab === 'pending' && (
<Resource name="pending" list={UserList} />
)}
{selectedTab === 'declined' && (
<Resource name="declined" list={UserList} />
)}
</Admin>
);
I tried to add customRoutes to with Resources required for each route, but no success.
This is not the way you should customize the side menu in React Admin. The Resources the user has access to should always be available as children of <Admin>.
Rather, you should follow the Using a Custom Menu doc to customize the way your side menu looks, and add the tab logic in there.

Clear ReferenceArrayInput selection on filter change

I have 2 ReferenceArrayInputs, one of which is wrapped in a FormDataConsumer.
The first one is for category, the second for sub-category. The second one depends on the first one so I conditionally render it.
When I select a value for category, then select a value for subcategory, and finally change the category value again I still see my selection in the subcategory.
<ArrayInput source={`shops.${shopIndex}.categories`}>
<SimpleFormIterator disableReordering>
<ReferenceInput source="categoryId" reference="categories">
<SelectInput optionText="name" />
</ReferenceInput>
<FormDataConsumer>
{({ formData, getSource, scopedFormData, ...rest }) => {
// No category, no render. I can't filter subcategories.
if (!scopedFormData.categoryId) {
return null;
}
return (
<SelectArrayInput
source={getSource('subCategoryIds')}
reference="subCategories"
perPage={500}
filter={{ categories: [scopedFormData.categoryId] }}
>
<ReferenceArrayInput optionText="name" />
</SelectArrayInput>
);
}}
</FormDataConsumer>
</SimpleFormIterator>
</ArrayInput>
Filtering works great, toggling visibility looks great... I just want to clear the subcategory value when changing the category.
I already tried useFormContext() with a setValue and resetValue, but neither worked. I'm not sure if this is a caching issue or simply because it's an array.
Regarding versions, this is 4.1.0 all the way up to the master branch (tried all of them).

How can I use related Filters in a List component?

I'm trying to use a kind of "related filters" in a <List> component inside my app.The idea is similar to a situation that you have a filter with country names and after you choose one, the second filter, will have the cities names related to the country in the first filter.
I made a custom component like this:
const CityFilter = props => {
return (
<Fragment>
<Country {...props} />
<FormDataConsumer>
{({ formData, ...rest }) => (
<SelectInput
{...props}
label="City"
source="cityId"
optionText="name"
optionValue="id"
choices={getCitiesfor(formData.countryId)}
parse={(v) => parseInt(v)}
{...rest}
/>
)}
</FormDataConsumer>
</Fragment>);
};
and it works good but the issue is that after you close the filter components the <List> still remains filtered.
So, there're any other way to do that? or how can i do to solve the issue?
Thanks in advance.

How do I implement Algolia Search filtering in a React Native Modal component?

I am using Algolia search in my React Native app, following the instructions in guides/building-search-ui/going-further/native/react
I have created a RefinementList as per the instructions, and this works fine when placed directly with the app, as per
<InstantSearch
searchClient={searchClient}
indexName={THE_INDEX}
root={root}
>
<RefinementList attribute="brand.name" />
<SearchBox />
<InfiniteHits />
</InstantSearch>
When this runs and I enter a search term I happily get a list of my faceted brand names. But I need this to work within a modal. So following Algolia's instructions I have created a Filters component and use my RefinementList from within that instead:
<InstantSearch
searchClient={searchClient}
indexName={THE_INDEX}
root={root}
searchState={this.state.searchState}
onSearchStateChange={this.onSearchStateChange}
>
<Filters
isModalOpen={this.state.isModalOpen}
toggleModal={this.toggleModal}
indexName={THE_INDEX}
searchClient={searchClient}
searchState={this.state.searchState}
onSearchStateChange={this.onSearchStateChange}
/>
<SearchBox />
<FilterButton
onFilter={this.toggleModal}
/>
<InfiniteHits />
</InstantSearch>
However the modal is always empty. The console shows that when the RefinementList is simply a child of the InstantSearch then its items array gets populated, but when the RefinementList is invoked within the Filters component then it's empty. The Filters component is exactly as per Algolia's docs, apart from the fact I pass in the indexName as a prop. I've checked in the React Native debugger and the RefinementList has access to the same context, the searchState is being set, and onSearchStateChange is being invoked correctly. The indexName is correct.
What am I doing wrong?
To fix this I had to add a 'virtual' SearchBox, ala
const VirtualSearchBox = connectSearchBox(() => null)
which is used as a child of the InstantSearch component in the Filters component, making Filters more like
const Filters = ({
isModalOpen,
searchState,
searchClient,
toggleModal,
onSearchStateChange,
indexName
}) => (
<Modal animationType="slide" visible={isModalOpen}>
<SafeAreaView>
<InstantSearch
searchClient={searchClient}
indexName={indexName}
searchState={searchState}
onSearchStateChange={onSearchStateChange}
>
<VirtualSearchBox />
<RefinementList attribute="brand" />
<TouchableOpacity style={styles.closeButton} onPress={toggleModal}>
<Text style={styles.closeButtonText}>Close</Text>
</TouchableOpacity>
</InstantSearch>
</SafeAreaView>
</Modal>
)
Inside the modal the searchState is always falsy because the SearchBox is not mounted.
Each time a request happens inside the modal the emtpy state will be provided.
The refinements inside the searchState are not enough to apply a refinement on the search parameters. All of the widgets associated with the refinement have to be mounted (ref: Search State docs).

React-Admin | Bulk-Select with ReferenceField

In our application, we're trying to use Datagrid within ReferenceField to create/modify/delete related records, as shown in https://marmelab.com/blog/2018/07/09/react-admin-tutorials-form-for-related-records.html
All the functionality shown in the tutorial works well, except the bulk-actions added in a recent react-admin update. Clicking these checkboxes gives
Uncaught TypeError: _this.props.onToggleItem is not a function
I believe this is because the onToggleItem prop is normally provided by the List component, however in this application, Datagrid doesn't have a parent List component.
Adding a List component between ReferenceManyField and Datagrid allows bulk select/delete to work after some changes to the style, however this causes another issue: the current displayed page (i.e. records 1-10, 11-20, etc) is stored per-resource in the store, and so it is possible to have a situation where the store says we're on page 2, and displays page 2, which is empty because there are only enough related items to fill one page.
Am I missing something here? Or is bulk-select inside ReferenceManyField not possible at the moment?
export const NetworksShow = (props) => (
<Show title={<NetworksTitle />} actions={<NetworksShowActions />} {...props}>
<ReferenceManyField addLabel={false} target="ipid" reference="acl-network">
<List style={{ margin: '0px -24px -16px -24px' }} {...props} actions={<NetworkACLCardActions ipid={props.id}/>} filter={{ ipid: _.has(props, 'id') ? props.id : undefined }}>
<Datagrid hasBulkActions={true}>
<ReferenceField label="Network" source="ipid" reference="networks">
<TextField source="name" />
</ReferenceField>
<TextField label="URL" source="url" />
<BWChip label="Action" source="wb" />
<EditButton />
<DeleteButton />
</Datagrid>
</List>
</ReferenceManyField>
</Show>
);
As a side-effect of https://github.com/marmelab/react-admin/pull/2365, it is now possible to use ReferenceManyField -> List -> Datagrid in the way described in the question.
For example, we're now doing the following:
<ReferenceManyField addLabel={false} target="groupid" reference="users">
<List
style={{ margin: '0px -24px -16px -24px' }}
filter={{ groupid: id }}
{...props}
>
<Datagrid hasBulkActions>
<LinkField label="Name" source="name" />
<LinkField label="Username" source="email" />
<FlexibleBooleanField label="Email" source="allowemail" />
<ACLChip label="User Access" source="aclid" />
</Datagrid>
</List>
</ReferenceManyField>
Bulk actions works with the above, and any issues with pagination are avoided as react-admin now checks and modifies pagination if nothing appears on the current page.
As I've understood from the documentation, Datagrid is just an iterator "dumb component".
It just "shows" things that the parent - usually List (connected component) or in your case ReferenceManyField - element previously has fetched.
Thus I think that BulkActions can only be functional when provided by a List element.
For the second part of your issue, Lists should be used top-level and not within other elements that's why it breaks your pagination.
I implemented "DumbList" which takes data from parent component instead of loading it itself. This solves the problem:
import React from 'react';
import { ListController } from 'ra-core';
import { ListView } from 'ra-ui-materialui/esm/list/List';
export const DumbList = props =>
<ListController {...props}>
{controllerProps => {
let { data } = props
const ids = Object.keys(data || {})
const total = ids.length
return <ListView
{...props}
// This is important, otherwise bulkActionsToolbar ends up on very top of the page
classes={{ card: 'relative' }}
{...Object.assign(controllerProps, { data, ids, total })} />
}}
</ListController>