Skip to content

Commit

Permalink
Add Feedback UI Components to Talawa Admin (#980)
Browse files Browse the repository at this point in the history
* Add feedback component

* Add tests for feedback modal

* Add handling for empty feedback

* Add Average Rating and Reviews component

* Add testing for all the added cards

* Fix tests and move to 100% coverage

* Add bugfix

* Add merge function to fix failing tests

* Add key definitons

* Change merge policy

* Add custom merge policy to all the Event Stat tests

* remove cache

* Migrate to a single query in the parent
  • Loading branch information
EshaanAgg authored Oct 4, 2023
1 parent 30e280d commit 5dffa1b
Show file tree
Hide file tree
Showing 17 changed files with 1,048 additions and 61 deletions.
319 changes: 261 additions & 58 deletions package-lock.json

Large diffs are not rendered by default.

5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@
"@apollo/client": "^3.4.0-beta.19",
"@apollo/link-error": "^2.0.0-beta.3",
"@apollo/react-testing": "^4.0.0",
"@emotion/react": "^11.11.0",
"@emotion/react": "^11.11.1",
"@emotion/styled": "^11.11.0",
"@mui/icons-material": "^5.8.3",
"@mui/material": "^5.13.3",
"@mui/material": "^5.14.1",
"@mui/x-charts": "^6.0.0-alpha.13",
"@mui/x-data-grid": "^6.8.0",
"@mui/x-date-pickers": "^6.6.0",
"@pdfme/generator": "^1.2.6",
Expand Down
8 changes: 8 additions & 0 deletions src/App.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,14 @@ import { CHECK_AUTH } from 'GraphQl/Queries/Queries';
import i18nForTest from './utils/i18nForTest';
import { StaticMockLink } from 'utils/StaticMockLink';

// Mock the modules for PieChart rendering as they require a trasformer being used (which is not done by Jest)
// These modules are used by the Feedback components
jest.mock('@mui/x-charts/PieChart', () => ({
pieArcLabelClasses: jest.fn(),
PieChart: jest.fn().mockImplementation(() => <>Test</>),
pieArcClasses: jest.fn(),
}));

const MOCKS = [
{
request: {
Expand Down
14 changes: 14 additions & 0 deletions src/GraphQl/Queries/Queries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,20 @@ export const EVENT_CHECKINS = gql`
}
`;

export const EVENT_FEEDBACKS = gql`
query eventFeedback($id: ID!) {
event(id: $id) {
_id
feedback {
_id
rating
review
}
averageFeedbackScore
}
}
`;

// Query to take the Organization with data
export const ORGANIZATIONS_LIST = gql`
query Organizations($id: ID!) {
Expand Down
62 changes: 62 additions & 0 deletions src/components/EventStats/EventStats.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import React from 'react';
import { render, waitFor } from '@testing-library/react';
import { MockedProvider } from '@apollo/react-testing';
import { EventStats } from './EventStats';
import { BrowserRouter } from 'react-router-dom';
import { EVENT_FEEDBACKS } from 'GraphQl/Queries/Queries';

// Mock the modules for PieChart rendering as they require a trasformer being used (which is not done by Jest)
// These modules are used by the Feedback component
jest.mock('@mui/x-charts/PieChart', () => ({
pieArcLabelClasses: jest.fn(),
PieChart: jest.fn().mockImplementation(() => <>Test</>),
pieArcClasses: jest.fn(),
}));

const mockData = [
{
request: {
query: EVENT_FEEDBACKS,
variables: {
id: 'eventStats123',
},
},
result: {
data: {
event: {
_id: 'eventStats123',
feedback: [
{
_id: 'feedback1',
review: 'review1',
rating: 5,
},
],
averageFeedbackScore: 5,
},
},
},
},
];

describe('Testing Event Stats', () => {
const props = {
eventId: 'eventStats123',
show: true,
handleClose: jest.fn(),
};

test('The stats should be rendered properly', async () => {
const { queryByText } = render(
<MockedProvider mocks={mockData} addTypename={false}>
<BrowserRouter>
<EventStats {...props} />
</BrowserRouter>
</MockedProvider>
);

await waitFor(() =>
expect(queryByText('Event Statistics')).toBeInTheDocument()
);
});
});
59 changes: 59 additions & 0 deletions src/components/EventStats/EventStats.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import React from 'react';
import { Modal } from 'react-bootstrap';
import { FeedbackStats } from './Statistics/Feedback';
import { ReviewStats } from './Statistics/Review';
import { AverageRating } from './Statistics/AverageRating';
import Stack from '@mui/material/Stack';
import styles from './Loader.module.css';
import { useQuery } from '@apollo/client';
import { EVENT_FEEDBACKS } from 'GraphQl/Queries/Queries';

type ModalPropType = {
show: boolean;
eventId: string;
handleClose: () => void;
};

export const EventStats = ({
show,
handleClose,
eventId,
}: ModalPropType): JSX.Element => {
const { data, loading } = useQuery(EVENT_FEEDBACKS, {
variables: { id: eventId },
});

// Render the loading screen
if (loading) {
return (
<>
<div className={styles.loader}></div>
</>
);
}

return (
<>
<Modal
show={show}
onHide={handleClose}
backdrop="static"
centered
size="lg"
>
<Modal.Header closeButton className="bg-primary">
<Modal.Title className="text-white">Event Statistics</Modal.Title>
</Modal.Header>
<Modal.Body>
<Stack direction="row" spacing={2}>
<FeedbackStats data={data} />
<div>
<ReviewStats data={data} />
<AverageRating data={data} />
</div>
</Stack>
</Modal.Body>
</Modal>
</>
);
};
76 changes: 76 additions & 0 deletions src/components/EventStats/EventStatsWrapper.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import React from 'react';
import { fireEvent, render, waitFor } from '@testing-library/react';
import { MockedProvider } from '@apollo/react-testing';
import { EventStatsWrapper } from './EventStatsWrapper';
import { BrowserRouter } from 'react-router-dom';
import { EVENT_FEEDBACKS } from 'GraphQl/Queries/Queries';

// Mock the modules for PieChart rendering as they require a trasformer being used (which is not done by Jest)
jest.mock('@mui/x-charts/PieChart', () => ({
pieArcLabelClasses: jest.fn(),
PieChart: jest.fn().mockImplementation(() => <>Test</>),
pieArcClasses: jest.fn(),
}));

const mockData = [
{
request: {
query: EVENT_FEEDBACKS,
variables: {
id: 'eventStats123',
},
},
result: {
data: {
event: {
_id: 'eventStats123',
feedback: [
{
_id: 'feedback1',
review: 'review1',
rating: 5,
},
],
averageFeedbackScore: 5,
},
},
},
},
];

// Mock the modules for PieChart rendering as they require a trasformer being used (which is not done by Jest)
// These modules are used by the Feedback component
jest.mock('@mui/x-charts/PieChart', () => ({
pieArcLabelClasses: jest.fn(),
PieChart: jest.fn().mockImplementation(() => <>Test</>),
pieArcClasses: jest.fn(),
}));

describe('Testing Event Stats Wrapper', () => {
const props = {
eventId: 'eventStats123',
};

test('The button to open and close the modal should work properly', async () => {
const { queryByText, queryByRole } = render(
<MockedProvider mocks={mockData} addTypename={false}>
<BrowserRouter>
<EventStatsWrapper {...props} />
</BrowserRouter>
</MockedProvider>
);

// Open the modal
fireEvent.click(queryByText('View Event Statistics') as Element);

await waitFor(() =>
expect(queryByText('Event Statistics')).toBeInTheDocument()
);

// Close the modal
fireEvent.click(queryByRole('button', { name: /close/i }) as HTMLElement);
await waitFor(() =>
expect(queryByText('Event Statistics')).not.toBeInTheDocument()
);
});
});
34 changes: 34 additions & 0 deletions src/components/EventStats/EventStatsWrapper.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import React, { useState } from 'react';
import { EventStats } from './EventStats';
import { Button } from 'react-bootstrap';

type PropType = {
eventId: string;
};

export const EventStatsWrapper = (props: PropType): JSX.Element => {
const [showModal, setShowModal] = useState(false);

return (
<>
<Button
type="button"
className="mt-3"
variant="success"
aria-label="eventStatistics"
onClick={(): void => {
setShowModal(true);
}}
>
View Event Statistics
</Button>
{showModal && (
<EventStats
show={showModal}
handleClose={(): void => setShowModal(false)}
eventId={props.eventId}
/>
)}
</>
);
};
43 changes: 43 additions & 0 deletions src/components/EventStats/Loader.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
.loader,
.loader:after {
border-radius: 50%;
width: 10em;
height: 10em;
}
.loader {
margin: 60px auto;
margin-top: 35vh !important;
font-size: 10px;
position: relative;
text-indent: -9999em;
border-top: 1.1em solid rgba(255, 255, 255, 0.2);
border-right: 1.1em solid rgba(255, 255, 255, 0.2);
border-bottom: 1.1em solid rgba(255, 255, 255, 0.2);
border-left: 1.1em solid #febc59;
-webkit-transform: translateZ(0);
-ms-transform: translateZ(0);
transform: translateZ(0);
-webkit-animation: load8 1.1s infinite linear;
animation: load8 1.1s infinite linear;
}

@-webkit-keyframes load8 {
0% {
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(360deg);
transform: rotate(360deg);
}
}
@keyframes load8 {
0% {
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(360deg);
transform: rotate(360deg);
}
}
58 changes: 58 additions & 0 deletions src/components/EventStats/Statistics/AverageRating.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import React from 'react';
import { render, waitFor } from '@testing-library/react';
import { AverageRating } from './AverageRating';
import { BrowserRouter } from 'react-router-dom';
import { Provider } from 'react-redux';
import { store } from 'state/store';
import { I18nextProvider } from 'react-i18next';
import i18nForTest from 'utils/i18nForTest';
import { ToastContainer } from 'react-toastify';

const props = {
data: {
event: {
_id: '123',
feedback: [
{
_id: 'feedback1',
review: 'review1',
rating: 5,
},
{
_id: 'feedback2',
review: 'review2',
rating: 5,
},
{
_id: 'feedback3',
review: null,
rating: 5,
},
],
averageFeedbackScore: 5,
},
},
};

describe('Testing Average Rating Card', () => {
test('The component should be rendered and the Score should be shown', async () => {
const { queryByText } = render(
<BrowserRouter>
<Provider store={store}>
<I18nextProvider i18n={i18nForTest}>
<ToastContainer />
<AverageRating {...props} />
</I18nextProvider>
</Provider>
</BrowserRouter>
);

await waitFor(() =>
expect(queryByText('Average Review Score')).toBeInTheDocument()
);

await waitFor(() =>
expect(queryByText('Rated 5.00 / 10')).toBeInTheDocument()
);
});
});
Loading

0 comments on commit 5dffa1b

Please sign in to comment.