Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix AutocompleteInput when record has a different shape between getList and getMany #8687

Merged
merged 3 commits into from
Mar 2, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions packages/ra-ui-materialui/src/input/AutocompleteInput.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { SimpleForm } from '../form';
import { AutocompleteInput } from './AutocompleteInput';
import { useCreateSuggestionContext } from './useSupportCreateSuggestion';
import {
DifferentShapeInGetMany,
InsideReferenceInput,
InsideReferenceInputDefaultValue,
Nullable,
Expand Down Expand Up @@ -1369,6 +1370,29 @@ describe('<AutocompleteInput />', () => {
);
});
});

it('should not reset the filter when typing when getMany returns a different record shape than getList', async () => {
render(<DifferentShapeInGetMany />);
await screen.findByDisplayValue('Leo Tolstoy');
const input = (await screen.findByLabelText(
'Author'
)) as HTMLInputElement;
expect(input.value).toBe('Leo Tolstoy');
fireEvent.mouseDown(input);
fireEvent.change(input, { target: { value: 'Leo Tolstoy test' } });
// Make sure that 'Leo Tolstoy' did not reappear
let testFailed = false;
try {
await waitFor(() => {
expect(input.value).toBe('Leo Tolstoy');
});
testFailed = true;
} catch {
// This is expected, nothing to do
}
expect(testFailed).toBe(false);
expect(input.value).toBe('Leo Tolstoy test');
});
});

it("should allow to edit the input if it's inside a FormDataConsumer", () => {
Expand Down
91 changes: 91 additions & 0 deletions packages/ra-ui-materialui/src/input/AutocompleteInput.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -875,3 +875,94 @@ export const NullishValuesSupport = () => {
</AdminContext>
);
};

const dataProviderWithDifferentShapeInGetMany = {
getOne: (resource, params) =>
Promise.resolve({
data: {
id: 1,
title: 'War and Peace',
author: 1,
summary:
"War and Peace broadly focuses on Napoleon's invasion of Russia, and the impact it had on Tsarist society. The book explores themes such as revolution, revolution and empire, the growth and decline of various states and the impact it had on their economies, culture, and society.",
year: 1869,
},
}),
getMany: (resource, params) =>
Promise.resolve({
data: authors
.filter(author => params.ids.includes(author.id))
.map(author => ({
...author,
newField: 'newField',
})),
}),
getList: (resource, params) =>
new Promise(resolve => {
// eslint-disable-next-line eqeqeq
if (params.filter.q == undefined) {
setTimeout(
() =>
resolve({
data: authors,
total: authors.length,
}),
500
);
return;
}

const filteredAuthors = authors.filter(author =>
author.name
.toLowerCase()
.includes(params.filter.q.toLowerCase())
);

setTimeout(
() =>
resolve({
data: filteredAuthors,
total: filteredAuthors.length,
}),
500
);
}),
update: (resource, params) => Promise.resolve(params),
create: (resource, params) => {
const newAuthor = {
id: authors.length + 1,
name: params.data.name,
language: params.data.language,
};
authors.push(newAuthor);
return Promise.resolve({ data: newAuthor });
},
} as any;

export const DifferentShapeInGetMany = () => (
<Admin
dataProvider={dataProviderWithDifferentShapeInGetMany}
history={history}
>
<Resource name="authors" />
<Resource
name="books"
edit={() => (
<Edit
mutationMode="pessimistic"
mutationOptions={{
onSuccess: data => {
console.log(data);
},
}}
>
<SimpleForm>
<ReferenceInput reference="authors" source="author">
<AutocompleteInput fullWidth optionText="name" />
</ReferenceInput>
</SimpleForm>
</Edit>
)}
/>
</Admin>
);
34 changes: 33 additions & 1 deletion packages/ra-ui-materialui/src/input/AutocompleteInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -727,7 +727,14 @@ const useSelectedChoice = <
multiple
);

if (!isEqual(selectedChoiceRef.current, newSelectedItems)) {
if (
!areSelectedItemsEqual(
selectedChoiceRef.current,
newSelectedItems,
optionValue,
multiple
)
) {
selectedChoiceRef.current = newSelectedItems;
setSelectedChoice(newSelectedItems);
}
Expand Down Expand Up @@ -757,4 +764,29 @@ const getSelectedItems = (
);
};

const areSelectedItemsEqual = (
selectedChoice: RaRecord | RaRecord[],
newSelectedChoice: RaRecord | RaRecord[],
optionValue = 'id',
multiple: boolean
) => {
if (multiple) {
const selectedChoiceArray = (selectedChoice as RaRecord[]) ?? [];
const newSelectedChoiceArray = (newSelectedChoice as RaRecord[]) ?? [];
if (selectedChoiceArray.length !== newSelectedChoiceArray.length) {
return false;
}
const equalityArray = selectedChoiceArray.map(choice =>
newSelectedChoiceArray.some(
newChoice =>
get(newChoice, optionValue) === get(choice, optionValue)
)
);
return !equalityArray.some(item => item === false);
}
return (
get(selectedChoice, optionValue) === get(newSelectedChoice, optionValue)
);
};

const DefaultFilterToQuery = searchText => ({ q: searchText });