From 79420c9040d7b002bab0f95a3be5002a9886ccf5 Mon Sep 17 00:00:00 2001 From: stdavis Date: Thu, 31 Oct 2024 13:02:52 -0600 Subject: [PATCH] fix: add spinners to UI elements that wait for network requests Closes #232 --- package-lock.json | 18 +++++++++++ package.json | 2 ++ src/components/MapContainer.tsx | 13 ++++++++ src/components/ResultsGrid.tsx | 8 +++-- src/components/filters/Purpose.tsx | 28 ++++++++++------- src/components/filters/SpeciesLength.tsx | 38 ++++++++++++++---------- 6 files changed, 80 insertions(+), 27 deletions(-) diff --git a/package-lock.json b/package-lock.json index c2d8452..52dac49 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,6 +18,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", @@ -37,6 +38,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", @@ -5235,6 +5237,16 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/lodash.debounce": { + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/@types/lodash.debounce/-/lodash.debounce-4.0.9.tgz", + "integrity": "sha512-Ma5JcgTREwpLRwMM+XwBR7DaWe96nC38uCBDFKZWbNKD+osjVzdpnUSwBcqCptrp16sSOLBAUb50Car5I0TCsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/lodash": "*" + } + }, "node_modules/@types/mdx": { "version": "2.0.13", "resolved": "https://registry.npmjs.org/@types/mdx/-/mdx-2.0.13.tgz", @@ -10079,6 +10091,12 @@ "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", "license": "MIT" }, + "node_modules/lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", + "license": "MIT" + }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", diff --git a/package.json b/package.json index c0fce6d..b65773b 100644 --- a/package.json +++ b/package.json @@ -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", @@ -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", diff --git a/src/components/MapContainer.tsx b/src/components/MapContainer.tsx index 2e25b7f..cd814b0 100644 --- a/src/components/MapContainer.tsx +++ b/src/components/MapContainer.tsx @@ -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 = { @@ -41,6 +43,7 @@ export const MapContainer = ({ onClick }: { onClick?: __esri.ViewImmediateClickE const [selectorOptions, setSelectorOptions] = useState(null); console.log('rendering MapContainer'); const { setMapView } = useMap(); + const [isDrawing, setIsDrawing] = useState(false); // setup the Map useEffect(() => { @@ -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(); @@ -99,6 +107,11 @@ export const MapContainer = ({ onClick }: { onClick?: __esri.ViewImmediateClickE return (
{selectorOptions?.view && } + {mapView.current && isDrawing && ( +
+ +
+ )}
); }; diff --git a/src/components/ResultsGrid.tsx b/src/components/ResultsGrid.tsx index 79ccdc5..3316115 100644 --- a/src/components/ResultsGrid.tsx +++ b/src/components/ResultsGrid.tsx @@ -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'; @@ -115,7 +115,11 @@ export default function ResultsGrid() { }); if (isPending) { - return loading...; + return ( +
+ +
+ ); } if (error) { return {error.message}; diff --git a/src/components/filters/Purpose.tsx b/src/components/filters/Purpose.tsx index 9e4c083..cc4190f 100644 --- a/src/components/filters/Purpose.tsx +++ b/src/components/filters/Purpose.tsx @@ -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([]); @@ -24,18 +24,26 @@ export default function Purpose(): JSX.Element { } }, [selectedValues, filterDispatch]); + if (error) { + return

{error.message}

; + } + return ( <>

Purpose

- - {purposesDomain.data?.map(({ name, code }) => ( -
- - -
- ))} -
+ {isPending ? ( + + ) : ( + + {data?.map(({ name, code }) => ( +
+ + +
+ ))} +
+ )}