diff --git a/decs.d.ts b/decs.d.ts index 68ef2ddb..af5e81c1 100644 --- a/decs.d.ts +++ b/decs.d.ts @@ -2,3 +2,4 @@ declare module '@gns-science/toshi-nest'; declare module 'react-csv'; declare module 'html-to-image'; declare module '*.md'; +declare module 'uuid'; diff --git a/package.json b/package.json index 6472d2ad..2ba31abe 100644 --- a/package.json +++ b/package.json @@ -1,11 +1,11 @@ { "name": "kororaa", - "version": "1.0.3", + "version": "1.0.4", "private": true, "dependencies": { "@emotion/react": "^11.9.0", "@emotion/styled": "^11.8.1", - "@gns-science/toshi-nest": "^3.6.5", + "@gns-science/toshi-nest": "^3.6.6", "@mui/icons-material": "^5.6.2", "@mui/material": "^5.6.4", "adblock-detect-react": "^1.1.0", @@ -13,6 +13,7 @@ "html-to-image": "^1.10.8", "markdown-to-jsx": "^7.1.7", "mathjs": "^10.5.3", + "mui-nested-menu": "^3.1.0", "react": "^18.1.0", "react-csv": "^2.2.2", "react-dom": "^18.1.0", @@ -25,6 +26,7 @@ "react-tooltip": "^4.2.21", "remark-gfm": "^3.0.1", "resize-observer-polyfill": "^1.5.1", + "uuid": "^9.0.0", "web-vitals": "^2.1.0" }, "scripts": { diff --git a/src/CHANGELOG.md b/src/CHANGELOG.md index a13dcc34..0b4e6d49 100644 --- a/src/CHANGELOG.md +++ b/src/CHANGELOG.md @@ -7,7 +7,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog). -## [v1.0.3](https://github.com/GNS-Science/kororaa/compare/v1.0.2...v1.0.3) +## [v1.0.4](https://github.com/GNS-Science/kororaa/compare/v1.0.3...v1.0.4) + +### Merged + +- Feature/287 Nested Hamburger, Update nest (Adds map overlays) [`#295`](https://github.com/GNS-Science/kororaa/pull/295) +- Feature/289_hazard_controls_errors [`#291`](https://github.com/GNS-Science/kororaa/pull/291) +- Deploy test [`#286`](https://github.com/GNS-Science/kororaa/pull/286) + +## [v1.0.3](https://github.com/GNS-Science/kororaa/compare/v1.0.2...v1.0.3) - 2022-11-10 ### Merged diff --git a/src/components/common/NavBar.tsx b/src/components/common/NavBar.tsx index 038dbbcc..d93c692f 100644 --- a/src/components/common/NavBar.tsx +++ b/src/components/common/NavBar.tsx @@ -2,7 +2,8 @@ import React from 'react'; import { styled } from '@mui/material/styles'; import { Link } from '@mui/material'; import { Link as RouterLink } from 'react-router-dom'; -import { AppBar, Typography, Container, Toolbar, IconButton, Box, Menu, MenuItem, Button } from '@mui/material'; +import { AppBar, Typography, Container, Toolbar, IconButton, Box, Menu, MenuItem, Button, Accordion, AccordionSummary, AccordionDetails } from '@mui/material'; +import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; import { useLocation } from 'react-router-dom'; import MenuIcon from '@mui/icons-material/Menu'; import ReactGA from 'react-ga4'; @@ -14,7 +15,6 @@ const StyledAppBar = styled(AppBar)(({ theme }) => ({ backgroundColor: theme.palette.navbar.main, height: 100, borderBottom: `5px solid ${theme.palette.navbar.accent}`, - // display: 'flex', alignItems: 'center', justifyContent: 'space-around', '&& .Mui-selected': { @@ -29,6 +29,16 @@ const StyledToolbar = styled(Toolbar)({ justifyContent: 'space-around', }); +const StyledAccordion = styled(Accordion)({ + boxShadow: 'none', + borderRadius: 0, + root: { + '&$expanded': { + margin: 'auto', + }, + }, +}); + interface MenuPageItem { name: string; path?: string; @@ -71,13 +81,50 @@ const SubMenu: React.FC = ({ pages, anchorElNav, setAnchorElNav, o open={open} onClose={handleCloseNavMenu} > - {pages.map((page) => ( - - - {page.name} - - - ))} + {pages.map((page) => { + if (page.submenu) { + return ( +
+ + } aria-controls="panel1a-content" id="panel1a-header"> + {page.name} + + + {page.submenu.map((subpage) => ( + { + handleCloseNavMenu(); + }} + selected={location.pathname === subpage.path} + > + {subpage.name} + + ))} + + +
+ ); + } else { + return ( + { + handleCloseNavMenu(); + }} + selected={location.pathname === page.path} + > + {page.name} + + ); + } + })} ); }; @@ -91,7 +138,7 @@ const HamburgerMenu: React.FC = ({ pages }: MenuProps) => { return ( <> - + @@ -191,20 +238,6 @@ const NavBar: React.FC = () => { }, ]; - const hamburgerPages = [ - { name: 'Curves and Spectra', path: '/HazardCurves' }, - { name: 'Disaggregations', path: '/Disaggs' }, - { name: 'Hazard Maps', path: '/HazardMaps' }, - { name: 'Coming Features', path: '/Previews' }, - { name: 'Science Reports', path: '/Resources/ScienceReports' }, - { name: 'Other Documents', path: '/Resources/OtherDocuments' }, - { name: 'Model Components', path: '/Resources/ModelComponents' }, - { name: 'About', path: '/About' }, - { name: 'Technical Info', path: '/TechInfo' }, - { name: 'Contacts', path: '/Contacts' }, - { name: 'Releases', path: '/Releases' }, - ]; - return ( @@ -215,7 +248,7 @@ const NavBar: React.FC = () => { {/*NSHM logo*/} - + diff --git a/src/components/common/SelectControlMultiple.tsx b/src/components/common/SelectControlMultiple.tsx index efd21cd6..cde6b604 100644 --- a/src/components/common/SelectControlMultiple.tsx +++ b/src/components/common/SelectControlMultiple.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { Checkbox, FormControl, Input, InputLabel, MenuItem, Select, SelectChangeEvent, Tooltip } from '@mui/material'; +import { Checkbox, FormControl, Input, InputLabel, MenuItem, Select, SelectChangeEvent, Tooltip, FormHelperText } from '@mui/material'; import { styled } from '@mui/material/styles'; const MultiSelectContainer = styled('div')({ @@ -14,9 +14,11 @@ export interface SelectControlMultiple { setSelection: (selections: string[]) => void; name: string; tooltip?: string; + error?: boolean; + errorMessage?: string; } -const SelectControlMultiple: React.FC = ({ options, selection, setSelection, name, tooltip = '' }: SelectControlMultiple) => { +const SelectControlMultiple: React.FC = ({ options, selection, setSelection, name, tooltip = '', error = false, errorMessage = '' }: SelectControlMultiple) => { const handleChange = (event: SelectChangeEvent) => { setSelection(event.target.value as string[]); }; @@ -48,6 +50,7 @@ const SelectControlMultiple: React.FC = ({ options, selec ))} + {error && {errorMessage}} ); @@ -76,6 +79,7 @@ const SelectControlMultiple: React.FC = ({ options, selec ))} + {error && {errorMessage}} ); diff --git a/src/views/hazardCharts/HazardChartsControls.tsx b/src/views/hazardCharts/HazardChartsControls.tsx index 787fbdd9..48a39fee 100644 --- a/src/views/hazardCharts/HazardChartsControls.tsx +++ b/src/views/hazardCharts/HazardChartsControls.tsx @@ -7,11 +7,11 @@ import PrintIcon from '@mui/icons-material/Print'; import CustomControlsBar from '../../components/common/CustomControlsBar'; import { hazardPageOptions } from './constants/hazardPageOptions'; -import { getPoeInputDisplay, numbersToStrings, stringsToNumbers, validateCurveGroupLength, validatePoeValue } from './hazardPage.service'; +import { getPoeInputDisplay, numbersToStrings, stringsToNumbers, validateCurveGroupLength, validateImts, validateLocationData, validatePoeValue, validateVs30s } from './hazardPage.service'; import { HazardPageState, LocationData } from './hazardPageReducer'; import SelectControlMultiple from '../../components/common/SelectControlMultiple'; import { getLatLonString, combineLocationData, getNamesFromLocationData, validateLatLon } from '../../services/latLon/latLon.service'; -import { locationTooltip, tooManyCurves, latLonTooltip } from './constants/hazardCharts'; +import { locationTooltip, tooManyCurves, latLonTooltip, noLocations, noVs30s, noImts } from './constants/hazardCharts'; import { imtTooltip, poeTooltip, vs30Tooltip } from '../../constants/tooltips'; interface HazardChartsControlsProps { @@ -37,6 +37,10 @@ const HazardChartsControls: React.FC = ({ state, disp const [controlsError, setControlsError] = useState(false); const [controlsErrorMessage, setControlsErrorMessage] = useState(''); + const [locationError, setLocationError] = useState(false); + const [vs30Error, setVs30Error] = useState(false); + const [imtError, setImtError] = useState(false); + useEffect(() => { const combinedLocationData = combineLocationData(locations, latLon); setLocationData(combinedLocationData); @@ -72,6 +76,9 @@ const HazardChartsControls: React.FC = ({ state, disp try { validatePoeValue(poeInput); validateLatLon(latLon); + validateLocationData(locationData, setLocationError); + validateVs30s(vs30s, setVs30Error); + validateImts(imts, setImtError); validateCurveGroupLength(locationData, vs30s, imts); dispatch({ locationData, vs30s, imts, poe: poeInput.length === 0 || poeInput === ' ' ? undefined : Number(poeInput) / 100 }); } catch (err) { @@ -81,6 +88,15 @@ const HazardChartsControls: React.FC = ({ state, disp } else if (err === tooManyCurves) { setControlsError(true); setControlsErrorMessage(err as string); + } else if (err === noLocations) { + setLocationError(true); + setControlsErrorMessage(err as string); + } else if (err === noVs30s) { + setVs30Error(true); + setControlsErrorMessage(err as string); + } else if (err === noImts) { + setImtError(true); + setControlsErrorMessage(err as string); } else { setPoeInputError(true); setPoeInputErrorMessage(err as string); @@ -90,7 +106,7 @@ const HazardChartsControls: React.FC = ({ state, disp return ( - + = ({ state, disp /> {latLonError && {latLonErrorMessage}} - setVs30s(stringsToNumbers(newValue))} - name="Vs30 (m/s)" - tooltip={vs30Tooltip} - /> + + setVs30s(stringsToNumbers(newValue))} + name="Vs30 (m/s)" + tooltip={vs30Tooltip} + /> + diff --git a/src/views/hazardCharts/constants/hazardCharts.ts b/src/views/hazardCharts/constants/hazardCharts.ts index cef0ed22..9f21e229 100644 --- a/src/views/hazardCharts/constants/hazardCharts.ts +++ b/src/views/hazardCharts/constants/hazardCharts.ts @@ -8,3 +8,9 @@ export const locationTooltip = 'Standard model locations are provided for larger export const latLonTooltip = `Coordinates in lat,lon form. For multiple locations use the \`;\` separator. Values will be rounded to the nearest location on the NSHM model grid.`; + +export const noLocations = 'Please enter at least one location'; + +export const noVs30s = 'Please select at least one Vs30'; + +export const noImts = 'Please select at least one spectral period'; diff --git a/src/views/hazardCharts/hazardPage.service.ts b/src/views/hazardCharts/hazardPage.service.ts index f2e583e7..3caa60f6 100644 --- a/src/views/hazardCharts/hazardPage.service.ts +++ b/src/views/hazardCharts/hazardPage.service.ts @@ -1,7 +1,7 @@ import { roundLatLon } from '../../services/latLon/latLon.service'; import { HAZARD_COLOR_LIMIT } from '../../utils/environmentVariables'; -import { tooManyCurves } from './constants/hazardCharts'; +import { tooManyCurves, noLocations, noImts, noVs30s } from './constants/hazardCharts'; import { hazardPageLocations } from './constants/hazardPageOptions'; import { LocationData } from './hazardPageReducer'; import { convertAgg } from '../../services/spectralAccel/spectralAccel.service'; @@ -207,3 +207,27 @@ export const sortCurveGroups = (curveGroups: HazardUncertaintyChartData): Hazard return sortedCurves; }; + +export const validateLocationData = (locationData: LocationData[], setLocationError: React.Dispatch>) => { + if (locationData.length === 0) { + throw noLocations; + } else { + setLocationError(false); + } +}; + +export const validateVs30s = (vs30s: number[], setVs30Error: React.Dispatch>) => { + if (vs30s.length === 0) { + throw noVs30s; + } else { + setVs30Error(false); + } +}; + +export const validateImts = (imts: string[], setImtError: React.Dispatch>) => { + if (imts.length === 0) { + throw noImts; + } else { + setImtError(false); + } +}; diff --git a/yarn.lock b/yarn.lock index b4f1f517..ff5304de 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1360,10 +1360,10 @@ minimatch "^3.1.2" strip-json-comments "^3.1.1" -"@gns-science/toshi-nest@^3.6.5": - version "3.6.5" - resolved "https://npm.pkg.github.com/download/@GNS-Science/toshi-nest/3.6.5/5599f8727dbef2390969578f94a0fbc916bd6231#5599f8727dbef2390969578f94a0fbc916bd6231" - integrity sha512-oxoLMlg3XID+qDjNicetn0oIcWHyE7qERP8/04GmM89bAszIsvuCF90NomyxYeTUB7+K8lIOqZ2hmpu89Vw28g== +"@gns-science/toshi-nest@^3.6.6": + version "3.6.6" + resolved "https://npm.pkg.github.com/download/@GNS-Science/toshi-nest/3.6.6/515b8b842dc69d7e8251056393cd531c36da2d21#515b8b842dc69d7e8251056393cd531c36da2d21" + integrity sha512-cRXlDkIu3lVbVYOvlAbvKTSPKZSOc5/9i6a8Ky30waVS+8x0zTJx0tW4Yd3rk/JH0AtnJZaerBv1BLYdvNDxlQ== dependencies: "@emotion/react" "^11.8.2" "@emotion/styled" "^11.8.1" @@ -9527,6 +9527,11 @@ msw@^0.42.1: type-fest "^1.2.2" yargs "^17.3.1" +mui-nested-menu@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/mui-nested-menu/-/mui-nested-menu-3.1.0.tgz#7f9a86c5ae2c0f5ff52d646dccf1312635cb06e2" + integrity sha512-no4Pp4QsmOhDcNM9hS6ZzGuOAfCGg0YsCir1latSpXNvMia0yovFE3XHKvmu9Dr2NradU06+C6rTIyTXuxtBqA== + multicast-dns@^7.2.5: version "7.2.5" resolved "https://registry.yarnpkg.com/multicast-dns/-/multicast-dns-7.2.5.tgz#77eb46057f4d7adbd16d9290fa7299f6fa64cced" @@ -13093,6 +13098,11 @@ uuid@^8.3.2: resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== +uuid@^9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.0.tgz#592f550650024a38ceb0c562f2f6aa435761efb5" + integrity sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg== + uvu@^0.5.0: version "0.5.6" resolved "https://registry.yarnpkg.com/uvu/-/uvu-0.5.6.tgz#2754ca20bcb0bb59b64e9985e84d2e81058502df"