diff --git a/src/components/Task/Modal/Form/Inputs/TagsAutocomplete/TagsAutocomplete.tsx b/src/components/Task/Modal/Form/Inputs/TagsAutocomplete/TagsAutocomplete.tsx index 17cb282973..b69ea6d9ae 100644 --- a/src/components/Task/Modal/Form/Inputs/TagsAutocomplete/TagsAutocomplete.tsx +++ b/src/components/Task/Modal/Form/Inputs/TagsAutocomplete/TagsAutocomplete.tsx @@ -13,6 +13,7 @@ interface TagsAutocompleteProps { value: string[]; onChange: (tagList: string[]) => void; label?: string; + selectedSuggestedTags?: string[]; } export const TagsAutocomplete: React.FC = ({ @@ -21,6 +22,7 @@ export const TagsAutocomplete: React.FC = ({ value, onChange, label, + selectedSuggestedTags, }) => { const { t } = useTranslation(); @@ -32,7 +34,11 @@ export const TagsAutocomplete: React.FC = ({ }); // Because of the @skip and @include directives, only contactTagList or taskTagList will be populated, but not both const options = - data?.accountList.contactTagList ?? data?.accountList.taskTagList ?? []; + data?.accountList.contactTagList ?? + data?.accountList?.taskTagList?.filter( + (tag) => !selectedSuggestedTags?.includes(tag), + ) ?? + []; return ( { }); }, 10000); + it('should toggle suggested tags and additional tags', async () => { + const { findByRole, getByRole, queryByRole } = render( + + + + mocks={{ + AssigneeOptions: { + accountListUsers: { + nodes: [ + { + user: { id: 'user-1', firstName: 'User', lastName: '1' }, + }, + { + user: { id: 'user-2', firstName: 'User', lastName: '2' }, + }, + ], + }, + }, + ContactOptions: { + contacts: { + nodes: [ + { id: 'contact-1', name: 'Contact 1' }, + { id: 'contact-2', name: 'Contact 2' }, + ], + }, + }, + TagOptions: { + accountList: { + taskTagList: ['tag-1', 'tag-2'], + }, + }, + }} + onCall={mutationSpy} + > + + + + , + ); + + userEvent.click(getByRole('combobox', { name: 'Task Type' })); + userEvent.click(await findByRole('option', { name: 'Appointment' })); + + expect(getByRole('combobox', { name: 'Tags' })).toBeInTheDocument(); + + userEvent.click(getByRole('combobox', { name: 'Tags' })); + userEvent.click(await findByRole('option', { name: 'tag-1' })); + + expect(getByRole('button', { name: 'tag-1' })).toBeInTheDocument(); + + userEvent.click(getByRole('combobox', { name: 'Tags' })); + await waitFor(() => + expect(queryByRole('option', { name: 'tag-1' })).not.toBeInTheDocument(), + ); + }); + it('persisted', async () => { const { getByRole, diff --git a/src/components/Task/Modal/Form/TaskModalForm.tsx b/src/components/Task/Modal/Form/TaskModalForm.tsx index 98da978f36..aa192701fa 100644 --- a/src/components/Task/Modal/Form/TaskModalForm.tsx +++ b/src/components/Task/Modal/Form/TaskModalForm.tsx @@ -1,5 +1,6 @@ import React, { ReactElement, + useCallback, useEffect, useMemo, useRef, @@ -391,6 +392,39 @@ const TaskModalForm = ({ setTimeout(() => activityRef?.current?.focus(), 50); }; + const handleChangeAutocomplete = useCallback( + (autoCompleteTagList: string[], tagList: string[]) => { + const suggested = autoCompleteTagList.filter((tag) => + phaseTags.some( + (phaseTag) => phaseTag.toLowerCase() === tag.toLowerCase(), + ), + ); + + if (suggested.length) { + setSelectedSuggestedTags([ + ...selectedSuggestedTags, + ...suggested.filter( + (tag) => + !selectedSuggestedTags + .map((t) => t.toLowerCase()) + .includes(tag.toLowerCase()), + ), + ]); + } + + const removedTag = tagList.find( + (tag) => !autoCompleteTagList.includes(tag), + ); + + if (removedTag && selectedSuggestedTags.includes(removedTag)) { + setSelectedSuggestedTags((prev) => + prev.filter((tag) => tag !== removedTag), + ); + } + }, + [phaseTags, setSelectedSuggestedTags, selectedSuggestedTags], + ); + return ( { + setSelectedSuggestedTags(tags); + // Remove any deselected suggested tags from tagList as well + setFieldValue( + 'tagList', + tagList.filter((tag) => tags.includes(tag)), + ); + }} /> )} setFieldValue('tagList', tagList)} + value={tagList || []} + onChange={(autoCompleteTagList) => { + setFieldValue('tagList', autoCompleteTagList); + handleChangeAutocomplete(autoCompleteTagList, tagList); + }} label={ phaseTags?.length && initialTask.completedAt ? t('Additional Tags')