Skip to content

Commit

Permalink
Merge pull request #7038 from marmelab/fix-autocomplete-array-input-o…
Browse files Browse the repository at this point in the history
…ptiontext

Fix AutocompleteArrayInput  optionText function not supported with create item set
  • Loading branch information
djhi authored Jan 3, 2022
2 parents b626d8a + 6370f08 commit 134209e
Show file tree
Hide file tree
Showing 4 changed files with 119 additions and 10 deletions.
2 changes: 2 additions & 0 deletions cypress/integration/edit.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ describe('Edit Page', () => {
EditPostTagsPage.navigate();
EditPostTagsPage.gotoTab(3);

cy.wait(250);

// Music is selected by default
cy.get(
EditPostTagsPage.elements.input('tags', 'reference-array-input')
Expand Down
100 changes: 100 additions & 0 deletions packages/ra-ui-materialui/src/input/AutocompleteArrayInput.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -924,4 +924,104 @@ describe('<AutocompleteArrayInput />', () => {

expect(queryByText('New Kid On The Block')).not.toBeNull();
});

it('should use optionText with a function value as text identifier when a create element is passed', () => {
const choices = [
{ id: 't', foobar: 'Technical' },
{ id: 'p', foobar: 'Programming' },
];
const newChoice = { id: 'js_fatigue', foobar: 'New Kid On The Block' };

const Create = () => {
const context = useCreateSuggestionContext();
const handleClick = () => {
choices.push(newChoice);
context.onCreate(newChoice);
};

return <button onClick={handleClick}>Get the kid</button>;
};

const { getByLabelText, getByText, queryAllByRole } = render(
<Form
onSubmit={jest.fn()}
render={() => (
<AutocompleteArrayInput
{...defaultProps}
create={<Create />}
optionText={choice => choice.foobar}
choices={choices}
/>
)}
/>
);

fireEvent.focus(
getByLabelText('resources.posts.fields.tags', {
selector: 'input',
})
);

expect(queryAllByRole('option')).toHaveLength(3);
expect(getByText('Technical')).not.toBeNull();
expect(getByText('Programming')).not.toBeNull();
expect(getByText('ra.action.create')).not.toBeNull();
});

it('should support creation of a new choice through the onCreate event when optionText is a function', async () => {
const choices = [
{ id: 'ang', name: 'Angular' },
{ id: 'rea', name: 'React' },
];
const handleCreate = filter => {
const newChoice = {
id: 'js_fatigue',
name: filter,
};
choices.push(newChoice);
return newChoice;
};

const { getByLabelText, getByText, queryByText, rerender } = render(
<Form
validateOnBlur
onSubmit={jest.fn()}
render={() => (
<AutocompleteArrayInput
source="language"
resource="posts"
choices={choices}
onCreate={handleCreate}
optionText={choice => `Choice is ${choice.name}`}
/>
)}
/>
);

const input = getByLabelText('resources.posts.fields.language', {
selector: 'input',
}) as HTMLInputElement;
input.focus();
fireEvent.change(input, { target: { value: 'New Kid On The Block' } });
fireEvent.click(getByText('Choice is ra.action.create_item'));
await new Promise(resolve => setTimeout(resolve));
rerender(
<Form
validateOnBlur
onSubmit={jest.fn()}
render={() => (
<AutocompleteArrayInput
source="language"
resource="posts"
resettable
choices={choices}
onCreate={handleCreate}
optionText={choice => `Choice is ${choice.name}`}
/>
)}
/>
);

expect(queryByText('Choice is New Kid On The Block')).not.toBeNull();
});
});
14 changes: 8 additions & 6 deletions packages/ra-ui-materialui/src/input/AutocompleteArrayInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,8 @@ const AutocompleteArrayInput = (props: AutocompleteArrayInputProps) => {
optionText,
});

const createItem = create || onCreate ? getCreateItem() : null;

const handleDelete = useCallback(
item => () => {
const newSelectedItems = [...selectedItems];
Expand Down Expand Up @@ -410,7 +412,7 @@ const AutocompleteArrayInput = (props: AutocompleteArrayInputProps) => {

const suggestions = [
...getSuggestions(suggestionFilter),
...(onCreate || create ? [getCreateItem()] : []),
...(!!createItem ? [createItem] : []),
];
return (
<div className={classes.container}>
Expand Down Expand Up @@ -438,8 +440,9 @@ const AutocompleteArrayInput = (props: AutocompleteArrayInputProps) => {
variant === 'outlined',
})}
>
{selectedItems.map(
(item, index) => (
{selectedItems
.filter(item => !!item)
.map((item, index) => (
<Chip
key={index}
tabIndex={-1}
Expand All @@ -451,8 +454,7 @@ const AutocompleteArrayInput = (props: AutocompleteArrayInputProps) => {
item
)}
/>
)
)}
))}
</div>
),
endAdornment: loading && (
Expand Down Expand Up @@ -519,7 +521,7 @@ const AutocompleteArrayInput = (props: AutocompleteArrayInputProps) => {
{suggestions.map((suggestion, index) => (
<AutocompleteSuggestionItem
key={getChoiceValue(suggestion)}
createValue={createValue}
createValue={createItem?.id}
suggestion={suggestion}
index={index}
highlightedIndex={highlightedIndex}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,15 @@ const AutocompleteSuggestionItem = (
} = props;
const classes = useStyles(props);
const isHighlighted = highlightedIndex === index;
const suggestionText =
'id' in suggestion && suggestion.id === createValue
? suggestion.name
: getSuggestionText(suggestion);
let suggestionText = getSuggestionText(suggestion);
if (
suggestionText === undefined &&
'id' in suggestion &&
suggestion.id === createValue
) {
suggestionText = suggestion.name;
}

let matches;
let parts;

Expand Down

0 comments on commit 134209e

Please sign in to comment.