-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Call API with add and remove buttons, refine loading and error strate…
…gies (#181, 191) (#196) * Set up api module; adopt react-async preliminarily * Replace react-async with react-async-hook; get CSRF token with js-cookie; add updateToolNav api call; wire up buttons; refactor error and loading handling * Move update API call state into ToolCard to enable overlapping actions * Use LinearProgress for loading, following accessibility instructions * Use canvas_id for key to eliminate warning * Log error in updateToolNav; add parentheses around statusText * Add eslint stuff for react-hooks, react-async-hook * Refactor useAsync, useAsyncCallback uses to fix error handling * Switch to react-query; use onSuccess options * Create ErrorsDisplay; simplify loading pattern * Remove margin from ErrorsDisplay; use Box with ErrorsDisplay in Home * Disable retry and refetch behavior in react-query (for now) * Remove comment * Add refresh alert; fix error spacing logic in Home * Make a couple minor ordering, formatting changes * Improve spacing with a feedbackBlock; move searchFilter to be with other state hooks * Reorder, group variables in ToolCard * Remove use of wildcard import with api * Remove length check in ErrorsDisplay * Remove extra newline * Use feedbackBlock pattern to eliminate unnecessary whitespace; remove extra space in import
- Loading branch information
Showing
9 changed files
with
449 additions
and
68 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
import Cookies from 'js-cookie'; | ||
|
||
import { Tool } from './interfaces'; | ||
|
||
const API_BASE = '/api'; | ||
const JSON_MIME_TYPE = 'application/json'; | ||
|
||
const BASE_MUTATION_HEADERS: HeadersInit = { | ||
Accept: JSON_MIME_TYPE, | ||
'Content-Type': JSON_MIME_TYPE, | ||
'X-Requested-With': 'XMLHttpRequest' | ||
}; | ||
|
||
const getCSRFToken = (): string | undefined => Cookies.get('csrftoken'); | ||
|
||
const createErrorMessage = async (res: Response): Promise<string> => { | ||
let errorBody; | ||
try { | ||
errorBody = await res.json(); | ||
} catch { | ||
console.error('Error body was not JSON.'); | ||
errorBody = undefined; | ||
} | ||
return ( | ||
'Error occurred! ' + | ||
`Status: ${res.status}` + (res.statusText !== '' ? ` (${res.statusText})` : '') + | ||
(errorBody !== undefined ? '; Body: ' + JSON.stringify(errorBody) : '.') | ||
); | ||
}; | ||
|
||
async function getTools (): Promise<Tool[]> { | ||
const url = `${API_BASE}/lti_tools/`; | ||
const res = await fetch(url); | ||
if (!res.ok) { | ||
console.error(res); | ||
throw new Error(await createErrorMessage(res)); | ||
} | ||
const data: Tool[] = await res.json(); | ||
return data; | ||
} | ||
|
||
interface UpdateToolNavData { | ||
canvasToolId: number | ||
navEnabled: boolean | ||
} | ||
|
||
async function updateToolNav (data: UpdateToolNavData): Promise<void> { | ||
const { canvasToolId, navEnabled } = data; | ||
const body = { navigation_enabled: navEnabled }; | ||
const url = `${API_BASE}/lti_tools/${canvasToolId}/`; | ||
const requestInit: RequestInit = { | ||
method: 'PUT', | ||
body: JSON.stringify(body), | ||
headers: { | ||
...BASE_MUTATION_HEADERS, | ||
'X-CSRFTOKEN': getCSRFToken() ?? '' | ||
} | ||
}; | ||
const res = await fetch(url, requestInit); | ||
if (!res.ok) { | ||
console.error(res); | ||
throw new Error(await createErrorMessage(res)); | ||
} | ||
return; | ||
} | ||
|
||
export { getTools, updateToolNav }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import React from 'react'; | ||
import { Alert, Grid } from '@mui/material'; | ||
|
||
interface ErrorsDisplayProps { | ||
errors: Error[] | ||
} | ||
|
||
export default function ErrorsDisplay (props: ErrorsDisplayProps) { | ||
return ( | ||
<Grid container spacing={1} justifyContent='center' direction='column'> | ||
{props.errors.map((e, i) => ( | ||
<Grid key={i} item><Alert severity='error'>{e.message}</Alert></Grid> | ||
))} | ||
</Grid> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,15 +1,29 @@ | ||
import React from 'react'; | ||
import ReactDOM from 'react-dom'; | ||
import { QueryClient, QueryClientProvider } from 'react-query'; | ||
import { ThemeProvider } from '@mui/material'; | ||
|
||
import Home from './components/Home'; | ||
import theme from './theme'; | ||
|
||
const queryClient = new QueryClient({ | ||
defaultOptions: { | ||
queries: { | ||
retry: false, | ||
retryOnMount: false, | ||
staleTime: Infinity | ||
}, | ||
mutations: { retry: false } | ||
} | ||
}); | ||
|
||
ReactDOM.render( | ||
( | ||
<ThemeProvider theme={theme}> | ||
<Home /> | ||
</ThemeProvider> | ||
<QueryClientProvider client={queryClient}> | ||
<ThemeProvider theme={theme}> | ||
<Home /> | ||
</ThemeProvider> | ||
</QueryClientProvider> | ||
), | ||
document.getElementById('react-app') | ||
); |
Oops, something went wrong.