Skip to content

Commit

Permalink
Merge pull request #23 from MetroStar/CSG-1080
Browse files Browse the repository at this point in the history
CSG-1080: Add React Query Support to Comet Starter App
  • Loading branch information
jbouder authored Sep 28, 2023
2 parents b9d4b27 + 0a5dec8 commit 2b50e99
Show file tree
Hide file tree
Showing 9 changed files with 74 additions and 197 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ The goal of this project is to provide a React with TypeScript starter applicati
- Code Formatting: [Prettier](https://prettier.io/)
- End-to-End (E2E) Testing: [Cypress](https://www.cypress.io/)
- Accessibility Testing: [cypress-axe](https://www.npmjs.com/package/cypress-axe)
- API support: [axios](https://axios-http.com/)
- API support: [Axios](https://axios-http.com/) with [React Query](https://tanstack.com/query/v3/)
- Authentication support: [Keycloak](https://www.keycloak.org/)

## Table of Contents
Expand Down
8 changes: 5 additions & 3 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import React from 'react';
import { Route, Routes } from 'react-router';
import { RecoilRoot } from 'recoil';
import { Footer } from './components/footer/footer';
import { Header } from './components/header/header';
import { ProtectedRoute } from './components/protected-route/protected-route';
Expand All @@ -9,8 +9,10 @@ import Details from './pages/details/details';
import { Home } from './pages/home/home';
import { SignIn } from './pages/sign-in/sign-in';

const queryClient = new QueryClient();

export const App = (): React.ReactElement => (
<RecoilRoot>
<QueryClientProvider client={queryClient}>
<div>
<Header />
<main id="mainSection" className="usa-section">
Expand All @@ -27,5 +29,5 @@ export const App = (): React.ReactElement => (
</main>
<Footer />
</div>
</RecoilRoot>
</QueryClientProvider>
);
85 changes: 0 additions & 85 deletions src/hooks/use-api.test.ts

This file was deleted.

57 changes: 0 additions & 57 deletions src/hooks/use-api.ts

This file was deleted.

5 changes: 4 additions & 1 deletion src/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React from 'react';
import ReactDOM from 'react-dom/client';
import { AuthProvider } from 'react-oidc-context';
import { BrowserRouter } from 'react-router-dom';
import { RecoilRoot } from 'recoil';
import { App } from './App.tsx';
import './styles.scss';
import keycloak from './utils/keycloak.ts';
Expand All @@ -10,7 +11,9 @@ ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<BrowserRouter>
<AuthProvider {...keycloak}>
<App />
<RecoilRoot>
<App />
</RecoilRoot>
</AuthProvider>
</BrowserRouter>
</React.StrictMode>,
Expand Down
27 changes: 15 additions & 12 deletions src/pages/dashboard/dashboard.test.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import axios from '@src/utils/axios';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { act, render } from '@testing-library/react';
import MockAdapter from 'axios-mock-adapter';
import { AuthProvider } from 'react-oidc-context';
Expand All @@ -9,11 +10,14 @@ import { User } from '../../types/user';
import { Dashboard } from './dashboard';

describe('Dashboard', () => {
const queryClient = new QueryClient();
const componentWrapper = (
<AuthProvider>
<RecoilRoot>
<BrowserRouter>
<Dashboard />
<QueryClientProvider client={queryClient}>
<Dashboard />
</QueryClientProvider>
</BrowserRouter>
</RecoilRoot>
</AuthProvider>
Expand All @@ -22,20 +26,19 @@ describe('Dashboard', () => {
const mock = new MockAdapter(axios);
beforeAll(() => {
mock.reset();
queryClient.setDefaultOptions({
queries: {
retry: false, // Disable retries for tests
},
});
});

test('should render successfully', async () => {
const { baseElement } = render(componentWrapper);
await act(async () => {
expect(baseElement).toBeTruthy();
expect(baseElement.querySelector('h1')?.textContent).toEqual(
'My Dashboard',
);
});
beforeEach(() => {
queryClient.clear();
});

test('should render with mock data', async () => {
mock.onGet().reply(200, { results: [] });
test('should render successfully', async () => {
mock.onGet(new RegExp('/?format=json')).reply(200, { results: [] });
jest.spyOn(useAuthMock, 'default').mockReturnValue({
isSignedIn: true,
currentUserData: {} as User,
Expand All @@ -59,7 +62,7 @@ describe('Dashboard', () => {
});

test('should render with error', async () => {
mock.onGet().networkError();
mock.onGet(new RegExp('/?format=json')).networkError();
const { baseElement } = render(componentWrapper);
await act(async () => {
expect(baseElement).toBeTruthy();
Expand Down
32 changes: 21 additions & 11 deletions src/pages/dashboard/dashboard.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,32 @@
import { Spinner } from '@metrostar/comet-extras';
import { Card, CardBody } from '@metrostar/comet-uswds';
import React, { useEffect } from 'react';
import { Launch } from '@src/types/launch';
import axios from '@src/utils/axios';
import { useQuery } from '@tanstack/react-query';
import React from 'react';
import ErrorNotification from '../../components/error-notification/error-notification';
import useApi from '../../hooks/use-api';
import useAuth from '../../hooks/use-auth';
import { DashboardBarChart } from './dashboard-bar-chart/dashboard-bar-chart';
import { DashboardPieChart } from './dashboard-pie-chart/dashboard-pie-chart';
import { DashboardTable } from './dashboard-table/dashboard-table';

export const Dashboard = (): React.ReactElement => {
const { isSignedIn } = useAuth();
const { getItems, items, loading, error } = useApi();

useEffect(() => {
if (isSignedIn) {
getItems();
}
}, [getItems, isSignedIn]);
const {
isLoading,
error,
data: items,
} = useQuery<Launch[], { message: string }>({
queryKey: ['launches'],
queryFn: () =>
axios
.get('/?format=json')
.then((response) => {
return response.data;
})
.then((data) => data.results),
enabled: isSignedIn,
});

return (
<div className="grid-container">
Expand All @@ -28,7 +38,7 @@ export const Dashboard = (): React.ReactElement => {
{error && (
<div className="grid-row padding-bottom-2">
<div className="grid-col">
<ErrorNotification error={error} />
<ErrorNotification error={error.message} />
</div>
</div>
)}
Expand All @@ -52,7 +62,7 @@ export const Dashboard = (): React.ReactElement => {
</div>
<div className="grid-row">
<div className="grid-col">
{loading ? (
{isLoading ? (
<Spinner id="spinner" type="small" loadingText="Loading..." />
) : (
<DashboardTable items={items} />
Expand Down
24 changes: 15 additions & 9 deletions src/pages/details/details.test.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import axios from '@src/utils/axios';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { act, render } from '@testing-library/react';
import MockAdapter from 'axios-mock-adapter';
import { AuthProvider } from 'react-oidc-context';
Expand All @@ -15,11 +16,14 @@ jest.mock('react-router-dom', () => ({
}));

describe('Details', () => {
const queryClient = new QueryClient();
const componentWrapper = (
<AuthProvider>
<RecoilRoot>
<BrowserRouter>
<Details />
<QueryClientProvider client={queryClient}>
<Details />
</QueryClientProvider>
</BrowserRouter>
</RecoilRoot>
</AuthProvider>
Expand All @@ -28,17 +32,19 @@ describe('Details', () => {
const mock = new MockAdapter(axios);
beforeAll(() => {
mock.reset();
queryClient.setDefaultOptions({
queries: {
retry: false, // Disable retries for tests
},
});
});

test('should render successfully', async () => {
const { baseElement } = render(componentWrapper);
await act(async () => {
expect(baseElement).toBeTruthy();
});
beforeEach(() => {
queryClient.clear();
});

test('should render with mock data', async () => {
mock.onGet().reply(200, launchData[0]);
test('should render successfully', async () => {
mock.onGet(new RegExp('/*/?format=json')).reply(200, launchData[0]);
jest.spyOn(useAuthMock, 'default').mockReturnValue({
isSignedIn: true,
currentUserData: {} as User,
Expand All @@ -57,7 +63,7 @@ describe('Details', () => {
});

test('should render with error', async () => {
mock.onGet().networkError();
mock.onGet(new RegExp('/*/?format=json')).networkError();
const { baseElement } = render(componentWrapper);
await act(async () => {
expect(baseElement).toBeTruthy();
Expand Down
Loading

0 comments on commit 2b50e99

Please sign in to comment.