Skip to content

Commit

Permalink
Merge pull request #4494 from marmelab/reference-array-input-selected…
Browse files Browse the repository at this point in the history
…-value

Fix ReferenceArrayInput list when filter is changed
  • Loading branch information
fzaninotto authored Mar 12, 2020
2 parents 3c6d944 + 50d8ac1 commit aaeff7f
Show file tree
Hide file tree
Showing 6 changed files with 139 additions and 20 deletions.
35 changes: 35 additions & 0 deletions cypress/integration/edit.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,41 @@ describe('Edit Page', () => {
expect(el).to.have.value(date)
);
});

it('should change reference list correctly when changing filter', () => {
const EditPostTagsPage = editPageFactory('/#/posts/13');
EditPostTagsPage.navigate();
EditPostTagsPage.gotoTab(3);

// Music is selected by default
cy.get(
EditPostTagsPage.elements.input('tags', 'reference-array-input')
)
.get(`div[role=button]`)
.contains('Music')
.should('exist');

EditPostTagsPage.clickInput('change-filter');

// Music should not be selected anymore after filter reset
cy.get(
EditPostTagsPage.elements.input('tags', 'reference-array-input')
)
.get(`div[role=button]`)
.contains('Music')
.should('not.exist');

EditPostTagsPage.clickInput('tags', 'reference-array-input');

// Music should not be visible in the list after filter reset
cy.get('div[role=listbox]')
.contains('Music')
.should('not.exist');

cy.get('div[role=listbox]')
.contains('Photo')
.should('exist');
});
});

it('should fill form correctly even when switching from one form type to another', () => {
Expand Down
7 changes: 5 additions & 2 deletions cypress/support/EditPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ export default url => ({
if (type === 'rich-text-input') {
return `.ra-input-${name} .ql-editor`;
}
if (type === 'reference-array-input') {
return `.ra-input div[role=combobox]`;
}
return `.edit-page [name='${name}']`;
},
inputs: `.ra-input`,
Expand Down Expand Up @@ -37,8 +40,8 @@ export default url => ({
}
},

clickInput(name) {
cy.get(this.elements.input(name)).click();
clickInput(name, type = 'input') {
cy.get(this.elements.input(name, type)).click();
},

gotoTab(index) {
Expand Down
11 changes: 4 additions & 7 deletions examples/simple/src/posts/PostEdit.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import RichTextInput from 'ra-input-rich-text';
import React from 'react';
import {
TopToolbar,
AutocompleteArrayInput,
AutocompleteInput,
ArrayInput,
BooleanInput,
Expand All @@ -18,7 +17,6 @@ import {
ImageField,
ImageInput,
NumberInput,
ReferenceArrayInput,
ReferenceManyField,
ReferenceInput,
SelectInput,
Expand All @@ -32,6 +30,7 @@ import {
FormDataConsumer,
} from 'react-admin'; // eslint-disable-line import/no-unresolved
import PostTitle from './PostTitle';
import TagReferenceInput from './TagReferenceInput';

const EditActions = ({ basePath, data, hasShow }) => (
<TopToolbar>
Expand Down Expand Up @@ -121,13 +120,11 @@ const PostEdit = ({ permissions, ...props }) => (
/>
</FormTab>
<FormTab label="post.form.miscellaneous">
<ReferenceArrayInput
<TagReferenceInput
reference="tags"
source="tags"
filter={{ published: true }}
>
<AutocompleteArrayInput fullWidth />
</ReferenceArrayInput>
label="Tags"
/>
<ArrayInput source="backlinks">
<SimpleFormIterator>
<DateInput source="date" />
Expand Down
46 changes: 46 additions & 0 deletions examples/simple/src/posts/TagReferenceInput.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import React, { useState } from 'react';
import { useForm } from 'react-final-form';
import { AutocompleteArrayInput, ReferenceArrayInput } from 'react-admin';
import Button from '@material-ui/core/Button';
import { makeStyles } from '@material-ui/core/styles';

const useStyles = makeStyles({
button: {
margin: '0 24px',
position: 'relative',
},
input: {
display: 'flex',
flexDirection: 'row',
justifyContent: 'flex-start',
width: '50%',
},
});

const TagReferenceInput = ({ ...props }) => {
const classes = useStyles();
const { change } = useForm();
const [filter, setFilter] = useState(true);

const handleAddFilter = () => {
setFilter(!filter);
change('tags', []);
};

return (
<div className={classes.input}>
<ReferenceArrayInput {...props} filter={{ published: filter }}>
<AutocompleteArrayInput />
</ReferenceArrayInput>
<Button
name="change-filter"
className={classes.button}
onClick={handleAddFilter}
>
Filter {filter ? 'Unpublished' : 'Published'} Tags
</Button>
</div>
);
};

export default TagReferenceInput;
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ describe('<ReferenceArrayInputController />', () => {
input={{ value: [1, 2] }}
>
{children}
</ReferenceArrayInputController>
</ReferenceArrayInputController>,
{ admin: { resources: { tags: { data: {} } } } }
);

expect(queryByText('true')).not.toBeNull();
Expand All @@ -43,7 +44,8 @@ describe('<ReferenceArrayInputController />', () => {
input={{ value: [1, 2] }}
>
{children}
</ReferenceArrayInputController>
</ReferenceArrayInputController>,
{ admin: { resources: { tags: { data: {} } } } }
);
expect(queryByText('true')).not.toBeNull();
});
Expand Down Expand Up @@ -110,6 +112,7 @@ describe('<ReferenceArrayInputController />', () => {
</ReferenceArrayInputController>,
{
admin: {
resources: { tags: { data: {} } },
references: {
possibleValues: {
'posts@tag_ids': { error: 'boom' },
Expand Down Expand Up @@ -234,6 +237,7 @@ describe('<ReferenceArrayInputController />', () => {
</ReferenceArrayInputController>,
{
admin: {
resources: { tags: { data: { 5: {}, 6: {} } } },
references: {
possibleValues: {
'posts@tag_ids': [],
Expand Down Expand Up @@ -288,7 +292,8 @@ describe('<ReferenceArrayInputController />', () => {
const { dispatch } = renderWithRedux(
<ReferenceArrayInputController {...defaultProps} allowEmpty>
{children}
</ReferenceArrayInputController>
</ReferenceArrayInputController>,
{ admin: { resources: { tags: { data: {} } } } }
);
expect(dispatch.mock.calls[0][0]).toEqual({
type: CRUD_GET_MATCHING,
Expand Down Expand Up @@ -425,7 +430,8 @@ describe('<ReferenceArrayInputController />', () => {
input={{ value: [5, 6] }}
>
{children}
</ReferenceArrayInputController>
</ReferenceArrayInputController>,
{ admin: { resources: { tags: { data: { 5: {}, 6: {} } } } } }
);
await wait(() => {
expect(dispatch).toHaveBeenCalledWith({
Expand All @@ -449,7 +455,8 @@ describe('<ReferenceArrayInputController />', () => {
input={{ value: [5] }}
>
{children}
</ReferenceArrayInputController>
</ReferenceArrayInputController>,
{ admin: { resources: { tags: { data: { 5: {} } } } } }
);

fireEvent.click(getByLabelText('Filter'));
Expand Down Expand Up @@ -477,7 +484,8 @@ describe('<ReferenceArrayInputController />', () => {
input={{ value: [5] }}
>
{children}
</ReferenceArrayInputController>
</ReferenceArrayInputController>,
{ admin: { resources: { tags: { data: { 5: {} } } } } }
);

rerender(
Expand Down Expand Up @@ -614,9 +622,9 @@ describe('<ReferenceArrayInputController />', () => {
input={{ value: [5, 6] }}
>
{children}
</ReferenceArrayInputController>
</ReferenceArrayInputController>,
{ admin: { resources: { tags: { data: { 5: {}, 6: {} } } } } }
);

await wait();
expect(dispatch).toHaveBeenCalledWith({
type: CRUD_GET_MANY,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,13 +56,43 @@ const useReferenceArrayInputController = ({
// optimization: we fetch selected items only once. When the user selects more items,
// as we already have the past selected items in the store, we don't fetch them.
useEffect(() => {
// Only fetch new ids
const newIdsToFetch = difference(input.value, inputValue.current);
if (newIdsToFetch.length > 0) {
// Only get from store ids selected and already fetched
const newIdsToGetFromStore = difference(input.value, newIdsToFetch);
/*
input.value (current)
+------------------------+
| ********************** |
| ********************** | inputValue.current (old)
| ********** +-----------------------+
| ********** | ooooooooo | |
| ********** | ooooooooo | |
| ********** | ooooooooo | |
| ********** | ooooooooo | |
+---|--------|------|----+ |
| | | |
| | | |
| +------|----------------+
| |
newIdsToFetch newIdsToGetFromStore
*/
// Change states each time input values changes to avoid keeping previous values no more selected
if (!isEqual(idsToFetch, newIdsToFetch)) {
setIdsToFetch(newIdsToFetch);
setIdsToGetFromStore(inputValue.current || []);
}
if (!isEqual(idsToGetFromStore, newIdsToGetFromStore)) {
setIdsToGetFromStore(newIdsToGetFromStore);
}

inputValue.current = input.value;
}, [input.value, setIdsToFetch]);
}, [
idsToFetch,
idsToGetFromStore,
input.value,
setIdsToFetch,
setIdsToGetFromStore,
]);

const [pagination, setPagination] = useState({ page: 1, perPage });
const [sort, setSort] = useState(defaultSort);
Expand Down

0 comments on commit aaeff7f

Please sign in to comment.