Skip to content

Commit

Permalink
fix: add spinners to UI elements that wait for network requests
Browse files Browse the repository at this point in the history
Closes #232
  • Loading branch information
stdavis committed Oct 31, 2024
1 parent b6cc397 commit 7ae9f91
Show file tree
Hide file tree
Showing 6 changed files with 80 additions and 27 deletions.
18 changes: 18 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
"firebase": "^11.0.1",
"immer": "^10.1.1",
"ky": "^1.7.2",
"lodash.debounce": "^4.0.8",
"react": "^18.3.1",
"react-aria": "^3.35.1",
"react-aria-components": "^1.4.1",
Expand All @@ -65,6 +66,7 @@
"@storybook/test": "^8.3.6",
"@tanstack/eslint-plugin-query": "^5.59.7",
"@types/eslint__js": "^8.42.3",
"@types/lodash.debounce": "^4.0.9",
"@types/react": "^18.3.11",
"@types/react-dom": "^18.3.1",
"@typescript-eslint/eslint-plugin": "^8.11.0",
Expand Down
13 changes: 13 additions & 0 deletions src/components/MapContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ import VectorTileLayer from '@arcgis/core/layers/VectorTileLayer';
import EsriMap from '@arcgis/core/Map';
import MapView from '@arcgis/core/views/MapView';
import LayerSelector from '@ugrc/layer-selector';
import debounce from 'lodash.debounce';
import { useEffect, useRef, useState } from 'react';
import { useMap } from './hooks';

import '@ugrc/layer-selector/src/LayerSelector.css';
import { Spinner } from '@ugrc/utah-design-system';
import config from '../config';

type LayerFactory = {
Expand Down Expand Up @@ -41,6 +43,7 @@ export const MapContainer = ({ onClick }: { onClick?: __esri.ViewImmediateClickE
const [selectorOptions, setSelectorOptions] = useState<SelectorOptions | null>(null);
console.log('rendering MapContainer');
const { setMapView } = useMap();
const [isDrawing, setIsDrawing] = useState(false);

// setup the Map
useEffect(() => {
Expand Down Expand Up @@ -79,6 +82,11 @@ export const MapContainer = ({ onClick }: { onClick?: __esri.ViewImmediateClickE

setSelectorOptions(selectorOptions);

mapView.current.watch(
'updating',
debounce((updating) => setIsDrawing(updating), 1000),
);

return () => {
mapView.current?.destroy();
mapComponent.current?.destroy();
Expand All @@ -99,6 +107,11 @@ export const MapContainer = ({ onClick }: { onClick?: __esri.ViewImmediateClickE
return (
<div ref={mapNode} className="size-full">
{selectorOptions?.view && <LayerSelector {...selectorOptions}></LayerSelector>}
{mapView.current && isDrawing && (
<div className="absolute left-[22px] top-[90px]">
<Spinner />
</div>
)}
</div>
);
};
8 changes: 6 additions & 2 deletions src/components/ResultsGrid.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useQuery } from '@tanstack/react-query';
import { useFirebaseAuth } from '@ugrc/utah-design-system';
import { Spinner, useFirebaseAuth } from '@ugrc/utah-design-system';
import { User } from 'firebase/auth';
import ky from 'ky';
import { TableBody } from 'react-aria-components';
Expand Down Expand Up @@ -115,7 +115,11 @@ export default function ResultsGrid() {
});

if (isPending) {
return <span>loading...</span>;
return (
<div className="flex h-full justify-center align-middle">
<Spinner />
</div>
);
}
if (error) {
return <span>{error.message}</span>;
Expand Down
28 changes: 18 additions & 10 deletions src/components/filters/Purpose.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { Button, Checkbox, CheckboxGroup } from '@ugrc/utah-design-system';
import { Button, Checkbox, CheckboxGroup, Spinner } from '@ugrc/utah-design-system';
import { useEffect, useState } from 'react';
import config from '../../config';
import { useFilter } from '../contexts/FilterProvider';
import { useDomainValues } from './utilities';

export default function Purpose(): JSX.Element {
const purposesDomain = useDomainValues(config.urls.events, config.fieldNames.SURVEY_PURPOSE);
const { data, isPending, error } = useDomainValues(config.urls.events, config.fieldNames.SURVEY_PURPOSE);
const { filterDispatch } = useFilter();
const [selectedValues, setSelectedValues] = useState<string[]>([]);

Expand All @@ -24,18 +24,26 @@ export default function Purpose(): JSX.Element {
}
}, [selectedValues, filterDispatch]);

if (error) {
return <p>{error.message}</p>;
}

return (
<>
<div>
<h3 className="text-lg font-semibold">Purpose</h3>
<CheckboxGroup onChange={setSelectedValues} value={selectedValues}>
{purposesDomain.data?.map(({ name, code }) => (
<div key={code} className="flex gap-1">
<Checkbox id={code} name={code} value={code} />
<label htmlFor={code}>{name}</label>
</div>
))}
</CheckboxGroup>
{isPending ? (
<Spinner />
) : (
<CheckboxGroup onChange={setSelectedValues} value={selectedValues}>
{data?.map(({ name, code }) => (
<div key={code} className="flex gap-1">
<Checkbox id={code} name={code} value={code} />
<label htmlFor={code}>{name}</label>
</div>
))}
</CheckboxGroup>
)}
</div>
<div className="w-30 flex justify-end">
<Button variant="secondary" onPress={() => setSelectedValues([])}>
Expand Down
38 changes: 23 additions & 15 deletions src/components/filters/SpeciesLength.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { MinusCircleIcon, PlusCircleIcon } from '@heroicons/react/16/solid';
import { Button, Select, SelectItem, TextField } from '@ugrc/utah-design-system';
import { Button, Select, SelectItem, Spinner, TextField } from '@ugrc/utah-design-system';
import { useEffect, useState } from 'react';
import config from '../../config';
import { useFilter } from '../contexts/FilterProvider';
Expand All @@ -20,24 +20,32 @@ interface RowControlsProps extends SpeciesLengthRow {

function RowControls({ species, min, max, onChange, addRow, removeRow, isLast }: RowControlsProps) {
const isInvalidRange = getIsInvalidRange(min, max);
const speciesDomain = useDomainValues(config.urls.fish, config.fieldNames.SPECIES_CODE);
const { data, isPending, error } = useDomainValues(config.urls.fish, config.fieldNames.SPECIES_CODE);

if (error) {
return <p>{error.message}</p>;
}

return (
<>
<div className="flex w-full items-end gap-1">
<Select
className="min-w-28"
label="Species"
onSelectionChange={(newValue) => onChange({ species: newValue as string, min, max })}
placeholder=" "
selectedKey={species}
>
{speciesDomain.data?.map(({ name, code }) => (
<SelectItem key={code} id={code}>
{name}
</SelectItem>
))}
</Select>
{isPending ? (
<Spinner />
) : (
<Select
className="min-w-28"
label="Species"
onSelectionChange={(newValue) => onChange({ species: newValue as string, min, max })}
placeholder=" "
selectedKey={species}
>
{data?.map(({ name, code }) => (
<SelectItem key={code} id={code}>
{name}
</SelectItem>
))}
</Select>
)}
<TextField
label="Min"
className="min-w-0 flex-grow"
Expand Down

0 comments on commit 7ae9f91

Please sign in to comment.