From d9d726944f3cc6d49a0cd80b385cee28cb331f08 Mon Sep 17 00:00:00 2001 From: Meetul Rathore Date: Mon, 28 Oct 2024 00:30:49 +0530 Subject: [PATCH] feat: Add people to tag functaionality (GSoC) (#2355) * add people to tag functaionality * minor change * more translations * minor change * add a variable for page size --- public/locales/en/translation.json | 9 +- public/locales/fr/translation.json | 9 +- public/locales/hi/translation.json | 9 +- public/locales/sp/translation.json | 9 +- public/locales/zh/translation.json | 9 +- src/GraphQl/Mutations/TagMutations.ts | 15 + src/GraphQl/Queries/userTagQueries.ts | 42 ++ .../AddPeopleToTag/AddPeopleToTag.module.css | 77 ++++ .../AddPeopleToTag/AddPeopleToTag.test.tsx | 198 +++++++++ .../AddPeopleToTag/AddPeopleToTag.tsx | 377 ++++++++++++++++++ .../AddPeopleToTag/AddPeopleToTagsMocks.ts | 169 ++++++++ src/screens/ManageTag/ManageTag.test.tsx | 7 +- src/screens/ManageTag/ManageTag.tsx | 43 +- src/screens/ManageTag/ManageTagMocks.ts | 84 ++++ src/utils/interfaces.ts | 39 +- src/utils/organizationTagsUtils.ts | 2 + 16 files changed, 1040 insertions(+), 58 deletions(-) create mode 100644 src/components/AddPeopleToTag/AddPeopleToTag.module.css create mode 100644 src/components/AddPeopleToTag/AddPeopleToTag.test.tsx create mode 100644 src/components/AddPeopleToTag/AddPeopleToTag.tsx create mode 100644 src/components/AddPeopleToTag/AddPeopleToTagsMocks.ts diff --git a/public/locales/en/translation.json b/public/locales/en/translation.json index a3415f199f..83bb999138 100644 --- a/public/locales/en/translation.json +++ b/public/locales/en/translation.json @@ -332,7 +332,14 @@ "successfullyUnassigned": "Tag unassigned from user", "addPeople": "Add People", "add": "Add", - "subTags": "Sub Tags" + "subTags": "Sub Tags", + "assignedToAll": "Tag Assigned to All", + "successfullyAssignedToPeople": "Tag assigned successfully", + "assignPeople": "Assign", + "errorOccurredWhileLoadingMembers": "Error occured while loading members", + "userName": "User Name", + "actions": "Actions", + "noOneSelected": "No One Selected" }, "userListCard": { "addAdmin": "Add Admin", diff --git a/public/locales/fr/translation.json b/public/locales/fr/translation.json index 6f7332057b..cd560dcda6 100644 --- a/public/locales/fr/translation.json +++ b/public/locales/fr/translation.json @@ -332,7 +332,14 @@ "successfullyUnassigned": "Étiquette retirée de l'utilisateur", "addPeople": "Ajouter des personnes", "add": "Ajouter", - "subTags": "Sous-étiquettes" + "subTags": "Sous-étiquettes", + "assignedToAll": "Étiquette attribuée à tous", + "successfullyAssignedToPeople": "Étiquette attribuée avec succès", + "assignPeople": "Attribuer", + "errorOccurredWhileLoadingMembers": "Erreur survenue lors du chargement des membres", + "userName": "Nom d'utilisateur", + "actions": "Actions", + "noOneSelected": "Personne sélectionnée" }, "userListCard": { "addAdmin": "Ajouter un administrateur", diff --git a/public/locales/hi/translation.json b/public/locales/hi/translation.json index 95d704daca..58d0aa5e57 100644 --- a/public/locales/hi/translation.json +++ b/public/locales/hi/translation.json @@ -332,7 +332,14 @@ "successfullyUnassigned": "उपयोगकर्ता से टैग हटा दिया गया", "addPeople": "लोगों को जोड़ें", "add": "जोड़ें", - "subTags": "उप-टैग्स" + "subTags": "उप-टैग्स", + "assignedToAll": "सभी को टैग असाइन किया गया", + "successfullyAssignedToPeople": "टैग सफलतापूर्वक असाइन किया गया", + "assignPeople": "असाइन करें", + "errorOccurredWhileLoadingMembers": "सदस्यों को लोड करते समय त्रुटि हुई", + "userName": "उपयोगकर्ता नाम", + "actions": "क्रियाएँ", + "noOneSelected": "कोई चयनित नहीं" }, "userListCard": { "addAdmin": "व्यवस्थापक जोड़ें", diff --git a/public/locales/sp/translation.json b/public/locales/sp/translation.json index bd2959808d..48c8f9940d 100644 --- a/public/locales/sp/translation.json +++ b/public/locales/sp/translation.json @@ -332,7 +332,14 @@ "successfullyUnassigned": "Etiqueta desasignada del usuario", "addPeople": "Agregar Personas", "add": "Agregar", - "subTags": "Subetiquetas" + "subTags": "Subetiquetas", + "assignedToAll": "Etiqueta asignada a todos", + "successfullyAssignedToPeople": "Etiqueta asignada con éxito", + "assignPeople": "Asignar", + "errorOccurredWhileLoadingMembers": "Error al cargar los miembros", + "userName": "Nombre de usuario", + "actions": "Acciones", + "noOneSelected": "Nadie seleccionado" }, "userListCard": { "joined": "Unido", diff --git a/public/locales/zh/translation.json b/public/locales/zh/translation.json index d5610594f6..9fb3965fd6 100644 --- a/public/locales/zh/translation.json +++ b/public/locales/zh/translation.json @@ -332,7 +332,14 @@ "successfullyUnassigned": "标签已从用户中取消分配", "addPeople": "添加人员", "add": "添加", - "subTags": "子标签" + "subTags": "子标签", + "assignedToAll": "标签分配给所有人", + "successfullyAssignedToPeople": "标签分配成功", + "assignPeople": "分配", + "errorOccurredWhileLoadingMembers": "加载成员时出错", + "userName": "用户名", + "actions": "操作", + "noOneSelected": "未选择任何人" }, "userListCard": { "addAdmin": "添加管理员", diff --git a/src/GraphQl/Mutations/TagMutations.ts b/src/GraphQl/Mutations/TagMutations.ts index 2e92babce4..d97fefc246 100644 --- a/src/GraphQl/Mutations/TagMutations.ts +++ b/src/GraphQl/Mutations/TagMutations.ts @@ -72,3 +72,18 @@ export const REMOVE_USER_TAG = gql` } } `; + +/** + * GraphQL mutation to add people to tag. + * + * @param tagId - Id of the tag to be assigned. + * @param userIds - Ids of the users to assign to. + */ + +export const ADD_PEOPLE_TO_TAG = gql` + mutation AddPeopleToUserTag($tagId: ID!, $userIds: [ID!]!) { + addPeopleToUserTag(input: { tagId: $tagId, userIds: $userIds }) { + _id + } + } +`; diff --git a/src/GraphQl/Queries/userTagQueries.ts b/src/GraphQl/Queries/userTagQueries.ts index e0673d81d7..9e1a11daa3 100644 --- a/src/GraphQl/Queries/userTagQueries.ts +++ b/src/GraphQl/Queries/userTagQueries.ts @@ -84,6 +84,48 @@ export const USER_TAG_SUB_TAGS = gql` } `; +/** + * GraphQL query to retrieve organization members that aren't assigned a certain tag. + * + * @param id - The ID of the tag. + * @returns The list of organization members. + */ + +export const USER_TAGS_MEMBERS_TO_ASSIGN_TO = gql` + query GetMembersToAssignTo( + $id: ID! + $after: String + $before: String + $first: PositiveInt + $last: PositiveInt + ) { + getUserTag(id: $id) { + name + usersToAssignTo( + after: $after + before: $before + first: $first + last: $last + ) { + edges { + node { + _id + firstName + lastName + } + } + pageInfo { + startCursor + endCursor + hasNextPage + hasPreviousPage + } + totalCount + } + } + } +`; + /** * GraphQL query to retrieve the ancestor tags of a certain tag. * diff --git a/src/components/AddPeopleToTag/AddPeopleToTag.module.css b/src/components/AddPeopleToTag/AddPeopleToTag.module.css new file mode 100644 index 0000000000..fb8599d96c --- /dev/null +++ b/src/components/AddPeopleToTag/AddPeopleToTag.module.css @@ -0,0 +1,77 @@ +.errorContainer { + min-height: 100vh; +} + +.errorMessage { + margin-top: 25%; + display: flex; + justify-content: center; + align-items: center; + flex-direction: column; +} + +.errorIcon { + transform: scale(1.5); + color: var(--bs-danger); + margin-bottom: 1rem; +} + +.tableHeader { + background-color: var(--bs-primary); + color: var(--bs-white); + font-size: 1rem; +} + +.rowBackground { + background-color: var(--bs-white); + max-height: 120px; +} + +.scrollContainer { + max-height: 100px; /* Adjust as needed */ + overflow-y: auto; + margin-bottom: 1rem; +} + +.memberBadge { + display: flex; + align-items: center; + padding: 5px 10px; + border-radius: 12px; + background-color: #f8f9fa; /* Light background */ + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); + max-width: calc(100% - 30px); /* Ensure it fits within the container */ +} + +.removeFilterIcon { + cursor: pointer; +} + +.scrollContainer { + max-height: 350px; /* Set your desired max height */ + overflow-y: auto; /* Enable vertical scrolling */ +} + +/* SimpleLoader.css */ +.simple-loader { + display: flex; + justify-content: center; + align-items: center; + width: 100%; + height: 100%; +} + +.spinner { + width: 40px; + height: 40px; + border: 4px solid rgba(0, 0, 0, 0.1); + border-top-color: #3498db; + border-radius: 50%; + animation: spin 0.6s linear infinite; +} + +@keyframes spin { + to { + transform: rotate(360deg); + } +} diff --git a/src/components/AddPeopleToTag/AddPeopleToTag.test.tsx b/src/components/AddPeopleToTag/AddPeopleToTag.test.tsx new file mode 100644 index 0000000000..4b24694a25 --- /dev/null +++ b/src/components/AddPeopleToTag/AddPeopleToTag.test.tsx @@ -0,0 +1,198 @@ +import React from 'react'; +import { MockedProvider } from '@apollo/react-testing'; +import type { RenderResult } from '@testing-library/react'; +import { + render, + screen, + fireEvent, + cleanup, + waitFor, +} from '@testing-library/react'; +import { Provider } from 'react-redux'; +import { MemoryRouter, Route, Routes } from 'react-router-dom'; +import 'jest-location-mock'; +import { I18nextProvider } from 'react-i18next'; + +import { store } from 'state/store'; +import userEvent from '@testing-library/user-event'; +import { StaticMockLink } from 'utils/StaticMockLink'; +import { toast } from 'react-toastify'; +import type { ApolloLink } from '@apollo/client'; +import type { InterfaceAddPeopleToTagProps } from './AddPeopleToTag'; +import AddPeopleToTag from './AddPeopleToTag'; +import i18n from 'utils/i18nForTest'; +import { MOCKS, MOCKS_ERROR } from './AddPeopleToTagsMocks'; + +const link = new StaticMockLink(MOCKS, true); +const link2 = new StaticMockLink(MOCKS_ERROR, true); + +async function wait(): Promise { + await waitFor(() => { + // The waitFor utility automatically uses optimal timing + return Promise.resolve(); + }); +} + +jest.mock('react-toastify', () => ({ + toast: { + success: jest.fn(), + error: jest.fn(), + }, +})); + +const translations = { + ...JSON.parse( + JSON.stringify(i18n.getDataByLanguage('en')?.translation.manageTag ?? {}), + ), + ...JSON.parse(JSON.stringify(i18n.getDataByLanguage('en')?.common ?? {})), + ...JSON.parse(JSON.stringify(i18n.getDataByLanguage('en')?.errors ?? {})), +}; + +const props: InterfaceAddPeopleToTagProps = { + addPeopleToTagModalIsOpen: true, + hideAddPeopleToTagModal: () => {}, + refetchAssignedMembersData: () => {}, + t: (key: string) => translations[key], + tCommon: (key: string) => translations[key], +}; + +const renderAddPeopleToTagModal = ( + props: InterfaceAddPeopleToTagProps, + link: ApolloLink, +): RenderResult => { + return render( + + + + + + } + /> + + + + + , + ); +}; + +describe('Organisation Tags Page', () => { + beforeEach(() => { + jest.mock('react-router-dom', () => ({ + ...jest.requireActual('react-router-dom'), + useParams: () => ({ orgId: 'orgId' }), + })); + // cache.reset(); + }); + + afterEach(() => { + jest.clearAllMocks(); + cleanup(); + }); + + test('Component loads correctly', async () => { + const { getByText } = renderAddPeopleToTagModal(props, link); + + await wait(); + + await waitFor(() => { + expect(getByText(translations.addPeople)).toBeInTheDocument(); + }); + }); + + test('Renders error component when when query is unsuccessful', async () => { + const { queryByText } = renderAddPeopleToTagModal(props, link2); + + await wait(); + + await waitFor(() => { + expect(queryByText(translations.addPeople)).not.toBeInTheDocument(); + }); + }); + + test('Selects and deselects members to assign to', async () => { + renderAddPeopleToTagModal(props, link); + + await wait(); + + await waitFor(() => { + expect(screen.getAllByTestId('selectMemberBtn')[0]).toBeInTheDocument(); + }); + userEvent.click(screen.getAllByTestId('selectMemberBtn')[0]); + + await waitFor(() => { + expect(screen.getAllByTestId('selectMemberBtn')[1]).toBeInTheDocument(); + }); + userEvent.click(screen.getAllByTestId('selectMemberBtn')[1]); + + await waitFor(() => { + expect( + screen.getAllByTestId('clearSelectedMember')[0], + ).toBeInTheDocument(); + }); + userEvent.click(screen.getAllByTestId('clearSelectedMember')[0]); + + await waitFor(() => { + expect(screen.getAllByTestId('deselectMemberBtn')[0]).toBeInTheDocument(); + }); + userEvent.click(screen.getAllByTestId('deselectMemberBtn')[0]); + }); + + test('Renders more members with infinite scroll', async () => { + const { getByText } = renderAddPeopleToTagModal(props, link); + + await wait(); + + await waitFor(() => { + expect(getByText(translations.addPeople)).toBeInTheDocument(); + }); + + // Find the infinite scroll div by test ID or another selector + const scrollableDiv = screen.getByTestId('scrollableDiv'); + + const initialMemberDataLength = screen.getAllByTestId('memberName').length; + + // Set scroll position to the bottom + fireEvent.scroll(scrollableDiv, { + target: { scrollY: scrollableDiv.scrollHeight }, + }); + + await waitFor(() => { + const finalMemberDataLength = screen.getAllByTestId('memberName').length; + expect(finalMemberDataLength).toBeGreaterThan(initialMemberDataLength); + + expect(getByText(translations.addPeople)).toBeInTheDocument(); + }); + }); + + test('Assigns tag to multiple people', async () => { + renderAddPeopleToTagModal(props, link); + + await wait(); + + await waitFor(() => { + expect(screen.getAllByTestId('selectMemberBtn')[0]).toBeInTheDocument(); + }); + userEvent.click(screen.getAllByTestId('selectMemberBtn')[0]); + + await waitFor(() => { + expect(screen.getAllByTestId('selectMemberBtn')[1]).toBeInTheDocument(); + }); + userEvent.click(screen.getAllByTestId('selectMemberBtn')[1]); + + await waitFor(() => { + expect(screen.getAllByTestId('selectMemberBtn')[2]).toBeInTheDocument(); + }); + userEvent.click(screen.getAllByTestId('selectMemberBtn')[2]); + + userEvent.click(screen.getByTestId('assignPeopleBtn')); + + await waitFor(() => { + expect(toast.success).toHaveBeenCalledWith( + translations.successfullyAssignedToPeople, + ); + }); + }); +}); diff --git a/src/components/AddPeopleToTag/AddPeopleToTag.tsx b/src/components/AddPeopleToTag/AddPeopleToTag.tsx new file mode 100644 index 0000000000..73066d2f0f --- /dev/null +++ b/src/components/AddPeopleToTag/AddPeopleToTag.tsx @@ -0,0 +1,377 @@ +import type { ApolloError } from '@apollo/client'; +import { useMutation, useQuery } from '@apollo/client'; +import type { GridCellParams, GridColDef } from '@mui/x-data-grid'; +import { DataGrid } from '@mui/x-data-grid'; +import Loader from 'components/Loader/Loader'; +import { USER_TAGS_MEMBERS_TO_ASSIGN_TO } from 'GraphQl/Queries/userTagQueries'; +import type { ChangeEvent } from 'react'; +import React, { useState } from 'react'; +import { Modal, Form, Button } from 'react-bootstrap'; +import { useParams } from 'react-router-dom'; +import type { InterfaceQueryUserTagsMembersToAssignTo } from 'utils/interfaces'; +import styles from './AddPeopleToTag.module.css'; +import { + ADD_PEOPLE_TO_TAGS_QUERY_LIMIT, + dataGridStyle, +} from 'utils/organizationTagsUtils'; +import { Stack } from '@mui/material'; +import { toast } from 'react-toastify'; +import { ADD_PEOPLE_TO_TAG } from 'GraphQl/Mutations/TagMutations'; +import InfiniteScroll from 'react-infinite-scroll-component'; +import { WarningAmberRounded } from '@mui/icons-material'; +import { useTranslation } from 'react-i18next'; + +/** + * Props for the `AddPeopleToTag` component. + */ +export interface InterfaceAddPeopleToTagProps { + addPeopleToTagModalIsOpen: boolean; + hideAddPeopleToTagModal: () => void; + refetchAssignedMembersData: () => void; + t: (key: string) => string; + tCommon: (key: string) => string; +} + +interface InterfaceMemberData { + _id: string; + firstName: string; + lastName: string; +} + +const AddPeopleToTag: React.FC = ({ + addPeopleToTagModalIsOpen, + hideAddPeopleToTagModal, + refetchAssignedMembersData, + t, + tCommon, +}) => { + const { tagId: currentTagId } = useParams(); + + const { t: tErrors } = useTranslation('error'); + + const [assignToMembers, setAssignToMembers] = useState( + [], + ); + + const { + data: userTagsMembersToAssignToData, + loading: userTagsMembersToAssignToLoading, + error: userTagsMembersToAssignToError, + fetchMore: fetchMoreMembersToAssignTo, + }: { + data?: { + getUserTag: InterfaceQueryUserTagsMembersToAssignTo; + }; + loading: boolean; + error?: ApolloError; + fetchMore: (options: { + variables: { + after?: string | null; + first?: number | null; + }; + updateQuery?: ( + previousQueryResult: { + getUserTag: InterfaceQueryUserTagsMembersToAssignTo; + }, + options: { + fetchMoreResult: { + getUserTag: InterfaceQueryUserTagsMembersToAssignTo; + }; + }, + ) => { getUserTag: InterfaceQueryUserTagsMembersToAssignTo }; + }) => Promise; + } = useQuery(USER_TAGS_MEMBERS_TO_ASSIGN_TO, { + variables: { + id: currentTagId, + first: ADD_PEOPLE_TO_TAGS_QUERY_LIMIT, + }, + skip: !addPeopleToTagModalIsOpen, + }); + + const loadMoreMembersToAssignTo = (): void => { + fetchMoreMembersToAssignTo({ + variables: { + first: ADD_PEOPLE_TO_TAGS_QUERY_LIMIT, + after: + userTagsMembersToAssignToData?.getUserTag.usersToAssignTo.pageInfo + .endCursor, // Fetch after the last loaded cursor + }, + updateQuery: ( + prevResult: { getUserTag: InterfaceQueryUserTagsMembersToAssignTo }, + { + fetchMoreResult, + }: { + fetchMoreResult: { + getUserTag: InterfaceQueryUserTagsMembersToAssignTo; + }; + }, + ) => { + if (!fetchMoreResult) return prevResult; + + return { + getUserTag: { + ...fetchMoreResult.getUserTag, + usersToAssignTo: { + ...fetchMoreResult.getUserTag.usersToAssignTo, + edges: [ + ...prevResult.getUserTag.usersToAssignTo.edges, + ...fetchMoreResult.getUserTag.usersToAssignTo.edges, + ], + }, + }, + }; + }, + }); + }; + + const userTagMembersToAssignTo = + userTagsMembersToAssignToData?.getUserTag.usersToAssignTo.edges.map( + (edge) => edge.node, + ); + + const handleAddOrRemoveMember = (member: InterfaceMemberData): void => { + setAssignToMembers((prevMembers) => { + const isAssigned = prevMembers.some((m) => m._id === member._id); + if (isAssigned) { + return prevMembers.filter((m) => m._id !== member._id); + } else { + return [...prevMembers, member]; + } + }); + }; + + const removeMember = (id: string): void => { + setAssignToMembers((prevMembers) => + prevMembers.filter((m) => m._id !== id), + ); + }; + + const [addPeople, { loading: addPeopleToTagLoading }] = + useMutation(ADD_PEOPLE_TO_TAG); + + const addPeopleToCurrentTag = async ( + e: ChangeEvent, + ): Promise => { + e.preventDefault(); + + try { + const { data } = await addPeople({ + variables: { + tagId: currentTagId, + userIds: assignToMembers.map((member) => member._id), + }, + }); + + if (data) { + toast.success(t('successfullyAssignedToPeople')); + refetchAssignedMembersData(); + hideAddPeopleToTagModal(); + setAssignToMembers([]); + } + } catch (error: unknown) { + /* istanbul ignore next */ + const errorMessage = + error instanceof Error ? error.message : tErrors('unknownError'); + toast.error(errorMessage); + } + }; + + if (userTagsMembersToAssignToError) { + return ( +
+
+ +
+ {t('errorOccurredWhileLoadingMembers')} +
+ {userTagsMembersToAssignToError.message} +
+
+
+ ); + } + + const columns: GridColDef[] = [ + { + field: 'id', + headerName: '#', + minWidth: 100, + align: 'center', + headerAlign: 'center', + headerClassName: `${styles.tableHeader}`, + sortable: false, + renderCell: (params: GridCellParams) => { + return
{params.row.id}
; + }, + }, + { + field: 'userName', + headerName: t('userName'), + flex: 2, + minWidth: 100, + sortable: false, + headerClassName: `${styles.tableHeader}`, + renderCell: (params: GridCellParams) => { + return ( +
+ {params.row.firstName + ' ' + params.row.lastName} +
+ ); + }, + }, + { + field: 'actions', + headerName: t('actions'), + flex: 1, + align: 'center', + minWidth: 100, + headerAlign: 'center', + sortable: false, + headerClassName: `${styles.tableHeader}`, + renderCell: (params: GridCellParams) => { + const isToBeAssigned = assignToMembers.some( + (member) => member._id === params.row._id, + ); + + return ( + + ); + }, + }, + ]; + + return ( + <> + + + {t('addPeople')} + +
+ + {userTagsMembersToAssignToLoading ? ( + + ) : ( + <> +
+ {assignToMembers.length === 0 ? ( +
+ {t('noOneSelected')} +
+ ) : ( + assignToMembers.map((member) => ( +
+ {member.firstName} {member.lastName} + removeMember(member._id)} + data-testid="clearSelectedMember" + /> +
+ )) + )} +
+ +
+ +
+
+ } + scrollableTarget="scrollableDiv" + > + row._id} + slots={{ + noRowsOverlay: /* istanbul ignore next */ () => ( + + {t('assignedToAll')} + + ), + }} + sx={dataGridStyle} + getRowClassName={() => `${styles.rowBackground}`} + autoHeight + rowHeight={65} + rows={userTagMembersToAssignTo?.map( + (membersToAssignTo, index) => ({ + id: index + 1, + ...membersToAssignTo, + }), + )} + columns={columns} + isRowSelectable={() => false} + /> +
+
+ + )} +
+ + + + + +
+ + ); +}; + +export default AddPeopleToTag; diff --git a/src/components/AddPeopleToTag/AddPeopleToTagsMocks.ts b/src/components/AddPeopleToTag/AddPeopleToTagsMocks.ts new file mode 100644 index 0000000000..ab185bb858 --- /dev/null +++ b/src/components/AddPeopleToTag/AddPeopleToTagsMocks.ts @@ -0,0 +1,169 @@ +import { ADD_PEOPLE_TO_TAG } from 'GraphQl/Mutations/TagMutations'; +import { USER_TAGS_MEMBERS_TO_ASSIGN_TO } from 'GraphQl/Queries/userTagQueries'; + +export const MOCKS = [ + { + request: { + query: USER_TAGS_MEMBERS_TO_ASSIGN_TO, + variables: { + id: '1', + first: 7, + }, + }, + result: { + data: { + getUserTag: { + name: 'tag1', + usersToAssignTo: { + edges: [ + { + node: { + _id: '1', + firstName: 'member', + lastName: '1', + }, + cursor: '1', + }, + { + node: { + _id: '2', + firstName: 'member', + lastName: '2', + }, + cursor: '2', + }, + { + node: { + _id: '3', + firstName: 'member', + lastName: '3', + }, + cursor: '3', + }, + { + node: { + _id: '4', + firstName: 'member', + lastName: '4', + }, + cursor: '4', + }, + { + node: { + _id: '5', + firstName: 'member', + lastName: '5', + }, + cursor: '5', + }, + { + node: { + _id: '6', + firstName: 'member', + lastName: '6', + }, + cursor: '6', + }, + { + node: { + _id: '7', + firstName: 'member', + lastName: '7', + }, + cursor: '7', + }, + ], + pageInfo: { + startCursor: '1', + endCursor: '7', + hasNextPage: true, + hasPreviousPage: false, + }, + totalCount: 10, + }, + }, + }, + }, + }, + { + request: { + query: USER_TAGS_MEMBERS_TO_ASSIGN_TO, + variables: { + id: '1', + first: 7, + after: '7', + }, + }, + result: { + data: { + getUserTag: { + name: 'tag1', + usersToAssignTo: { + edges: [ + { + node: { + _id: '8', + firstName: 'member', + lastName: '8', + }, + cursor: '8', + }, + { + node: { + _id: '9', + firstName: 'member', + lastName: '9', + }, + cursor: '9', + }, + { + node: { + _id: '10', + firstName: 'member', + lastName: '10', + }, + cursor: '10', + }, + ], + pageInfo: { + startCursor: '8', + endCursor: '10', + hasNextPage: false, + hasPreviousPage: true, + }, + totalCount: 10, + }, + }, + }, + }, + }, + { + request: { + query: ADD_PEOPLE_TO_TAG, + variables: { + tagId: '1', + userIds: ['1', '3', '5'], + }, + }, + result: { + data: { + addPeopleToUserTag: { + _id: '1', + }, + }, + }, + }, +]; + +export const MOCKS_ERROR = [ + { + request: { + query: USER_TAGS_MEMBERS_TO_ASSIGN_TO, + variables: { + id: '1', + first: 7, + }, + }, + error: new Error('Mock Graphql Error'), + }, +]; diff --git a/src/screens/ManageTag/ManageTag.test.tsx b/src/screens/ManageTag/ManageTag.test.tsx index 38ebba7402..d497d2a0a8 100644 --- a/src/screens/ManageTag/ManageTag.test.tsx +++ b/src/screens/ManageTag/ManageTag.test.tsx @@ -60,6 +60,7 @@ const cache = new InMemoryCache({ getUserTag: { keyArgs: false, merge(existing = {}, incoming) { + console.log(existing); return incoming; }, }, @@ -99,7 +100,7 @@ const renderManageTag = (link: ApolloLink): RenderResult => { ); }; -describe('Organisation Tags Page', () => { +describe('Manage Tag Page', () => { beforeEach(() => { jest.mock('react-router-dom', () => ({ ...jest.requireActual('react-router-dom'), @@ -290,7 +291,9 @@ describe('Organisation Tags Page', () => { userEvent.click(screen.getByTestId('unassignTagModalSubmitBtn')); await waitFor(() => { - expect(toast.success).toBeCalledWith(translations.successfullyUnassigned); + expect(toast.success).toHaveBeenCalledWith( + translations.successfullyUnassigned, + ); }); }); }); diff --git a/src/screens/ManageTag/ManageTag.tsx b/src/screens/ManageTag/ManageTag.tsx index 86a44fb169..25b39e884d 100644 --- a/src/screens/ManageTag/ManageTag.tsx +++ b/src/screens/ManageTag/ManageTag.tsx @@ -23,6 +23,7 @@ import { USER_TAG_ANCESTORS, USER_TAGS_ASSIGNED_MEMBERS, } from 'GraphQl/Queries/userTagQueries'; +import AddPeopleToTag from 'components/AddPeopleToTag/AddPeopleToTag'; /** * Component that renders the Manage Tag screen when the app navigates to '/orgtags/:orgId/managetag/:tagId'. @@ -432,41 +433,13 @@ function ManageTag(): JSX.Element { {/* Add People To Tag Modal */} - - - {t('addPeople')} - -
- - - - - - -
-
+ {/* Unassign Tag Modal */}