diff --git a/src/components/SettingsDropdowns/SettingsDropdowns.js b/src/components/SettingsDropdowns/SettingsDropdowns.js index 14f5ed6c9..8bd795ed1 100644 --- a/src/components/SettingsDropdowns/SettingsDropdowns.js +++ b/src/components/SettingsDropdowns/SettingsDropdowns.js @@ -1,9 +1,13 @@ +/* eslint-disable react/jsx-props-no-spreading */ import styled from '@emotion/styled'; -import { Checkbox, ListItem, TextField, Typography } from '@mui/material'; +import { + Checkbox, ListItem, TextField, Typography, +} from '@mui/material'; import PropTypes from 'prop-types'; import React, { useRef, useState } from 'react'; import { useIntl } from 'react-intl'; import { useDispatch, useSelector } from 'react-redux'; +import { visuallyHidden } from '@mui/utils'; import config from '../../../config'; import { resetMobilityTreeSelections } from '../../redux/actions/mobilityTree'; import { resetServiceTreeSelections } from '../../redux/actions/serviceTree'; @@ -29,7 +33,7 @@ import SMButton from '../ServiceMapButton'; import constants from '../SettingsComponent/constants'; import SMAutocomplete from '../SMAutocomplete'; -const SettingsDropdowns = ({ variant = 'default'}) => { +function SettingsDropdowns({ variant }) { const intl = useIntl(); const dispatch = useDispatch(); const getLocaleText = useLocaleText(); @@ -37,6 +41,8 @@ const SettingsDropdowns = ({ variant = 'default'}) => { // Format settings from redux to easier structure const settingsValues = constants.convertToSettingsValues(settings); const [openSettings, setOpenSettings] = useState(null); + const [resetText, setResetText] = useState(''); + const highlightedOption = useRef(null); const themeMode = useSelector(selectThemeMode); const ownSettingsVariant = variant === 'ownSettings'; @@ -61,7 +67,7 @@ const SettingsDropdowns = ({ variant = 'default'}) => { { id: organization.id, title: getLocaleText(organization.name) } )); - const toggleSettingsBox = (id) => { + const toggleSettingsBox = id => { if (openSettings === id) setOpenSettings(null); else setOpenSettings(id); }; @@ -70,6 +76,9 @@ const SettingsDropdowns = ({ variant = 'default'}) => { if (!id) { return; } + + setResetText(''); + if (category === 'mobility') { dispatch(setMobility(id)); setOpenSettings(null); @@ -122,6 +131,8 @@ const SettingsDropdowns = ({ variant = 'default'}) => { dispatch(resetCustomPosition()); dispatch(changeTheme('default')); dispatch(resetUserPosition()); + + setResetText(intl.formatMessage({ id: 'settings.reset_button.ariaLive' })); }; const handleKeyboardSelect = (id, category, event) => { @@ -171,8 +182,9 @@ const SettingsDropdowns = ({ variant = 'default'}) => { ChipProps={{ clickable: true, onDelete: null, variant: ownSettingsVariant ? 'outlined' : 'filled', }} - slotProps={{ - popper:{ sx: { pb: 1 } } // This padding fixes the listBox position on small screens where the list is renderend to top of input + slotProps={{ + // eslint-disable-next-line max-len + popper: { sx: { pb: 1 } }, // This padding fixes the listBox position on small screens where the list is renderend to top of input }} renderOption={(props, option) => (isSingleOption ? ( // Single option options box @@ -188,12 +200,11 @@ const SettingsDropdowns = ({ variant = 'default'}) => { /> {option.title} - )) - } + ))} renderInput={({ inputProps, ...rest }) => ( { + onClick={e => { e?.stopPropagation(); toggleSettingsBox(label); }} @@ -222,6 +233,7 @@ const SettingsDropdowns = ({ variant = 'default'}) => { {renderSettingsElement(citySettingsList, intl.formatMessage({ id: 'settings.choose.cities' }), 'cities')} {renderSettingsElement(organizationSettingsList, intl.formatMessage({ id: 'settings.choose.organization' }), 'organizations')}
+ {resetText} {
); -}; +} const StyledButton = styled(SMButton)(() => ({ marginRight: 0 })); @@ -280,6 +292,10 @@ const StyledAutocomplete = styled(SMAutocomplete)(({ theme, ownsettings, colormo return { ...styles, ...ownSettingsStyles }; }); +SettingsDropdowns.defaultProps = { + variant: 'default', +}; + SettingsDropdowns.propTypes = { variant: PropTypes.oneOf(['default', 'ownSettings']), }; diff --git a/src/components/TabLists/TabLists.js b/src/components/TabLists/TabLists.js index ed52bfad2..6db9b1a25 100644 --- a/src/components/TabLists/TabLists.js +++ b/src/components/TabLists/TabLists.js @@ -1,3 +1,4 @@ +/* eslint-disable react/forbid-prop-types */ /* eslint-disable react/no-multi-comp */ import React, { useState, useEffect, useRef } from 'react'; import PropTypes from 'prop-types'; @@ -16,14 +17,14 @@ import config from '../../../config'; import useMobileStatus from '../../utils/isMobile'; import PaginatedList from '../Lists/PaginatedList'; -const TabLists = ({ +function TabLists({ location, data, - onTabChange = null, - focusClass = null, - focusText = null, - headerComponents = null, -}) => { + onTabChange, + focusClass, + focusText, + headerComponents, +}) { const isMobile = useMobileStatus(); const theme = useTheme(); const navigator = useSelector(selectNavigator); @@ -154,7 +155,7 @@ const TabLists = ({ const renderHeader = () => { let fullData = []; - data.forEach((element) => { + data.forEach(element => { if (element.data && !element.noOrderer) { fullData = [...fullData, ...element.data]; } @@ -216,16 +217,16 @@ const TabLists = ({ } { fullData.length > 0 && ( - <> - - + ) } { focusClass && focusText && ( - {focusText} + + {focusText} + ) } { - filteredData.map((item, index) => { - if (item.data && item.data.length > 0) { - const label = `${item.title} ${item.component ? '' : `(${item.data.length})`}`.trim(); - const tabId = `${item.title}-${item.data.length}`; - return ( - item.onClick(index) : null} - focusVisibleClassName={tabFocusClass} - /> - ); - } + filteredData.map((item, index) => { + if (item.data && item.data.length > 0) { + const label = `${item.title} ${item.component ? '' : `(${item.data.length})`}`.trim(); + const tabId = `${item.title}-${item.data.length}`; + return ( - item.onClick(index) : null} focusVisibleClassName={tabFocusClass} /> ); - }) - } + } + return ( + item.onClick(index) : null} + focusVisibleClassName={tabFocusClass} + /> + ); + }) + } ); @@ -281,18 +285,18 @@ const TabLists = ({ useEffect(() => { calculateHeaderStylings(); + // eslint-disable-next-line react-hooks/exhaustive-deps }, [isMobile]); - useEffect(() => { // Change tab if selected tab is changed on url const tabFromUrl = getTabfromUrl(); if (tabFromUrl !== tabIndex) { handleTabChange(null, tabFromUrl); } + // eslint-disable-next-line react-hooks/exhaustive-deps }, [location]); - const render = () => ( <> { @@ -359,7 +363,7 @@ const TabLists = ({ ); return render(); -}; +} const StyledTabs = styled(Tabs)(({ theme }) => ({ position: 'sticky', @@ -404,4 +408,11 @@ TabLists.propTypes = { focusText: PropTypes.string, }; +TabLists.defaultProps = { + onTabChange: null, + focusClass: null, + focusText: null, + headerComponents: null, +}; + export default TabLists; diff --git a/src/i18n/en.js b/src/i18n/en.js index 7d96737b7..787ed1eec 100644 --- a/src/i18n/en.js +++ b/src/i18n/en.js @@ -724,6 +724,7 @@ export default { 'settings.choose.organization': 'Choose a service provider', 'settings.map.info': 'You can select the background map that best suits you in the map settings.', 'settings.reset_button.title': 'Clear all my selections', + 'settings.reset_button.ariaLive': 'All selections were cleared.', // Tools 'tool.download': 'Download data', diff --git a/src/i18n/fi.js b/src/i18n/fi.js index c7a73f670..76021d5db 100644 --- a/src/i18n/fi.js +++ b/src/i18n/fi.js @@ -726,6 +726,7 @@ export default { 'settings.choose.organization': 'Valitse palveluntarjoaja', 'settings.map.info': 'Kartta-asetuksista voit valita sinulle parhaiten sopivan pohjakartan.', 'settings.reset_button.title': 'Tyhjennä kaikki valintani', + 'settings.reset_button.ariaLive': 'Kaikki valinnat tyhjennettiin.', // Tools 'tool.download': 'Lataa tiedot', diff --git a/src/i18n/sv.js b/src/i18n/sv.js index ab5979e52..14aa2cb18 100644 --- a/src/i18n/sv.js +++ b/src/i18n/sv.js @@ -725,6 +725,7 @@ export default { 'settings.choose.organization': 'Välj tjänsteleverantör', 'settings.map.info': 'I kartinställningarna kan du välja det kartunderlag som passar dig bäst.', 'settings.reset_button.title': 'Rensa alla mina val', + 'settings.reset_button.ariaLive': 'Alla val har rensats.', // Tools 'tool.download': 'Exportera', diff --git a/src/views/ServiceTreeView/ServiceTreeView.js b/src/views/ServiceTreeView/ServiceTreeView.js index 80b350011..d54573e7a 100644 --- a/src/views/ServiceTreeView/ServiceTreeView.js +++ b/src/views/ServiceTreeView/ServiceTreeView.js @@ -1,11 +1,12 @@ -import { css } from '@emotion/css'; +/* eslint-disable react/forbid-prop-types */ import styled from '@emotion/styled'; import { Search } from '@mui/icons-material'; -import { Checkbox, List, Typography } from '@mui/material'; -import { useTheme } from '@mui/styles'; +import { List, Typography } from '@mui/material'; import PropTypes from 'prop-types'; import React, { useEffect, useState } from 'react'; import { useDispatch, useSelector } from 'react-redux'; +import { Checkbox as HDSCheckbox } from 'hds-react'; +import { visuallyHidden } from '@mui/utils'; import config from '../../../config'; import { SMAccordion, SMButton, TitleBar } from '../../components'; import { setMobilityTree } from '../../redux/actions/mobilityTree'; @@ -44,7 +45,7 @@ const getVariantDependentVariables = (variant, serviceTreeServices, mobilityTree }; }; -const ServiceTreeView = ({ intl, variant }) => { +function ServiceTreeView({ intl, variant }) { const navigator = useSelector(selectNavigator); const citySettings = useSelector(selectSelectedCities); const organizationSettings = useSelector(selectSelectedOrganizations); @@ -61,7 +62,6 @@ const ServiceTreeView = ({ intl, variant }) => { const dispatch = useDispatch(); const getLocaleText = useLocaleText(); const isMobile = useMobileStatus(); - const theme = useTheme(); const { serviceApi, titleKey, @@ -88,7 +88,7 @@ const ServiceTreeView = ({ intl, variant }) => { const nodeObjects = node.children.map(child => services.find(e => e.id === child)); nodes.push(...nodeObjects); // Check if any child nodes are opened to repeat this function on them - nodeObjects.forEach((obj) => { + nodeObjects.forEach(obj => { if (obj?.id && opened.some(item => item === obj.id)) { nodes.push(...checkChildNodes(obj)); } @@ -108,7 +108,9 @@ const ServiceTreeView = ({ intl, variant }) => { const fetchNodeCounts = async (nodes, fullSearch) => { const idList = nodes.map(node => node.id); // Do not fetch unit counts again for nodes that have the data, unless specified by fullSearch - const filteredIdList = fullSearch ? idList : idList.filter(id => !unitCounts.some(count => count.id === id)) + const filteredIdList = fullSearch ? idList : idList + .filter(id => !unitCounts.some(count => count.id === id)); + const smAPI = new ServiceMapAPI(); const fetchOptions = {}; if (organizationSettings.length) { @@ -119,17 +121,17 @@ const ServiceTreeView = ({ intl, variant }) => { fetchOptions.municipality = citySettings; } const counts = await Promise.all( - filteredIdList.map(async (id) => { + filteredIdList.map(async id => { const count = await smAPI.serviceNodeSearch(variant, id, fetchOptions, true); return { id, count }; }), ); if (fullSearch) { - setUnitCounts(counts) + setUnitCounts(counts); } else { - setUnitCounts([...unitCounts, ...counts]) + setUnitCounts([...unitCounts, ...counts]); } - } + }; const setInitialServices = () => { // Fetch initially shown service nodes when first entering the pag @@ -142,11 +144,11 @@ const ServiceTreeView = ({ intl, variant }) => { }); }; - const fetchChildServices = async (service) => { + const fetchChildServices = async service => { // Fetch and set to state the child nodes of the opened node fetch(`${serviceApi}?parent=${service}&page=1&page_size=1000`) .then(response => response.json()) - .then((data) => { + .then(data => { setServices([...services, ...data.results]); if (variant === SERVICE_TREE) { fetchNodeCounts(data.results); @@ -183,13 +185,12 @@ const ServiceTreeView = ({ intl, variant }) => { } if (child?.children) { data.push(...child.children); - child.children.forEach((c) => { + child.children.forEach(c => { getSelectedChildNodes(c, data); }); } return data; }; - const handleExpand = (service, isOpen) => { if (isOpen) { // Close expanded item setOpened(opened.filter(e => e !== service.id)); @@ -215,7 +216,7 @@ const ServiceTreeView = ({ intl, variant }) => { setSelected(selected.filter(element => element.id !== item.id)); } - // If checbox is not checked, add checkbox selections + // If checbox is not checked, add checkbox selections } else { // Select all visible child nodes as well let newState = [item, ...checkChildNodes(item)]; @@ -280,6 +281,7 @@ const ServiceTreeView = ({ intl, variant }) => { if (!services.length) { setInitialServices(); } + // eslint-disable-next-line react-hooks/exhaustive-deps }, []); useEffect(() => { @@ -287,6 +289,7 @@ const ServiceTreeView = ({ intl, variant }) => { setUnitCounts([]); fetchNodeCounts(services, true); } + // eslint-disable-next-line react-hooks/exhaustive-deps }, [citySettings, organizationSettings]); function calculateTitle(item) { @@ -295,7 +298,7 @@ const ServiceTreeView = ({ intl, variant }) => { } // Calculate count - const countItem = unitCounts.find(countItem => countItem.id === item.id) + const countItem = unitCounts.find(countItem => countItem.id === item.id); return `${getLocaleText(item.name)} ${countItem !== null && countItem !== undefined ? `(${countItem.count})` : ''}`; } @@ -314,10 +317,6 @@ const ServiceTreeView = ({ intl, variant }) => { const childIsSelected = checkChildNodes(item) .some(node => selected.some(item => item.id === node.id)); - const checkBoxFocusClass = css({ - boxShadow: `inset 0 0 0 4px ${theme.palette.primary.main} !important`, - }); - return (
  • { {level > 0 && (drawOuterLines(level, last, item.id))} {drawCheckboxLines(isOpen, level, item.id)} - handleCheckboxClick(e, item)} - icon={} - color="primary" + {checkboxSrTitle}} checked={isSelected} indeterminate={childIsSelected && !isSelected} + onChange={e => handleCheckboxClick(e, item)} + style={{ '--size': '1rem' }} /> @@ -381,7 +380,7 @@ const ServiceTreeView = ({ intl, variant }) => { // If node's parent is also checked, add only parent to list of selected nodes for search const selectedList = []; - selected.forEach((e) => { + selected.forEach(e => { if (!selected.some(i => i.id === e.parent)) { selectedList.push(e); } @@ -421,7 +420,7 @@ const ServiceTreeView = ({ intl, variant }) => { ); -}; +} const StyledFlexContainer = styled.div(() => ({ display: 'flex', @@ -474,15 +473,6 @@ const StyledGuidanceInfoText = styled(Typography)(({ theme }) => ({ textAlign: 'left', })); -const StyledCheckBoxIcon = styled('span')(() => ({ - margin: -1, - width: 15, - height: 15, - backgroundColor: '#fff', - border: '1px solid #323232;', - borderRadius: 1, -})); - const StyledCheckBox = styled('div')(() => ({ width: 40, height: '100%', diff --git a/src/views/UnitView/UnitView.js b/src/views/UnitView/UnitView.js index 86c68b96b..7b799b5f7 100644 --- a/src/views/UnitView/UnitView.js +++ b/src/views/UnitView/UnitView.js @@ -1,6 +1,9 @@ +/* eslint-disable react/forbid-prop-types */ /* eslint-disable no-underscore-dangle */ import styled from '@emotion/styled'; -import { Hearing, Mail, OpenInFull, Share } from '@mui/icons-material'; +import { + Hearing, Mail, OpenInFull, Share, +} from '@mui/icons-material'; import { Button, Typography } from '@mui/material'; import { visuallyHidden } from '@mui/utils'; import Watermark from '@uiw/react-watermark'; @@ -56,7 +59,7 @@ import UnitLinks from './components/UnitLinks'; import UnitsServicesList from './components/UnitsServicesList'; import { parseUnitViewUrlParams } from './utils/unitViewUrlParamAndSettingsHandler'; -const UnitView = (props) => { +function UnitView(props) { const { embed = false, match = {}, @@ -87,6 +90,7 @@ const UnitView = (props) => { const getLocaleText = useLocaleText(); const dispatch = useDispatch(); const history = useHistory(); + const accessiblityTabRef = useRef(); const getImageAlt = () => `${intl.formatMessage({ id: 'unit.picture' })}${getLocaleText(unit.name)}`; @@ -117,11 +121,12 @@ const UnitView = (props) => { dispatch(setMapType(value)); }); resetUrlSearchParams(); + // eslint-disable-next-line react-hooks/exhaustive-deps }, []); const initializePTVAccessibilitySentences = () => { if (unit) { - unit.identifiers.forEach((element) => { + unit.identifiers.forEach(element => { if (element.namespace === 'ptv') { const ptvId = element.value; fetchAccessibilitySentences(ptvId); @@ -135,7 +140,7 @@ const UnitView = (props) => { const unitId = params.unit; // If no selected unit data, or selected unit data is old, fetch new data if (!stateUnit || !checkCorrectUnit(stateUnit) || !stateUnit.complete) { - fetchSelectedUnit(unitId, (unit) => { + fetchSelectedUnit(unitId, unit => { setUnit(unit); if (unit?.keywords?.fi?.some(keyword => keyword.toLowerCase() === 'kuuluvuuskartta')) { fetchHearingMaps(unitId); @@ -214,18 +219,21 @@ const UnitView = (props) => { map.setView(viewPosition?.current?.center, viewPosition?.current?.zoom); } }; + // eslint-disable-next-line react-hooks/exhaustive-deps }, []); useEffect(() => { // If unit changes without the component unmounting, update data if (unit) { intializeUnitData(); } + // eslint-disable-next-line react-hooks/exhaustive-deps }, [match.params.unit]); useEffect(() => { if (config.usePtvAccessibilityApi) { initializePTVAccessibilitySentences(); } + // eslint-disable-next-line react-hooks/exhaustive-deps }, [unit]); if (embed) { @@ -311,7 +319,7 @@ const UnitView = (props) => { const renderDetailTab = () => { if (!unit || !unit.complete) { - return <>; + return null; } const contractText = UnitHelper.getContractText(unit, intl, getLocaleText); @@ -347,7 +355,11 @@ const UnitView = (props) => { {/* View Components */} - + { const renderServiceTab = () => { if (!unit || !unit.complete) { - return <>; + return null; } return ( @@ -464,7 +476,7 @@ const UnitView = (props) => { color="primary" aria-label={intl.formatMessage({ id: 'map.button.expand.aria' })} icon={} - onClick={(e) => { + onClick={e => { e.preventDefault(); if (navigator) { navigator.openMap(); @@ -481,7 +493,6 @@ const UnitView = (props) => { ); - const render = () => { const title = unit && unit.name ? getLocaleText(unit.name) : ''; const onLinkOpenClick = () => { @@ -515,10 +526,9 @@ const UnitView = (props) => { titleComponent="h3" shareLink={elem} /> - {unit?.location?.coordinates && - - } - + {unit?.location?.coordinates + && } + ); if (unitFetching) { @@ -551,6 +561,7 @@ const UnitView = (props) => { data: null, itemsPerPage: null, title: intl.formatMessage({ id: 'accessibility' }), + ref: accessiblityTabRef, }, { id: 'services', @@ -584,7 +595,7 @@ const UnitView = (props) => { : renderPicture() } - )} + )} /> ); @@ -603,7 +614,7 @@ const UnitView = (props) => { }; return render(); -}; +} export default UnitView; diff --git a/src/views/UnitView/components/ContactInfo/ContactInfo.js b/src/views/UnitView/components/ContactInfo/ContactInfo.js index e9efc11b3..adf4253ae 100644 --- a/src/views/UnitView/components/ContactInfo/ContactInfo.js +++ b/src/views/UnitView/components/ContactInfo/ContactInfo.js @@ -1,3 +1,4 @@ +/* eslint-disable react/forbid-prop-types */ import styled from '@emotion/styled'; import React from 'react'; import PropTypes from 'prop-types'; @@ -14,11 +15,9 @@ import useLocaleText from '../../../../utils/useLocaleText'; import { parseSearchParams, stringifySearchParams } from '../../../../utils'; import { SMAccordion } from '../../../../components'; -const ContactInfo = ({ - unit, - userLocation = null, - headingLevel = 'h4', -}) => { +function ContactInfo({ + unit, userLocation, headingLevel, accessiblityTabRef, +}) { const intl = useIntl(); const history = useHistory(); const location = useLocation(); @@ -36,7 +35,9 @@ const ContactInfo = ({ const phone = { type: 'PHONE', value: unit.phone ? { phone: unit.phone } : intl.formatMessage({ id: 'unit.phone.missing' }), - noDivider: showCallInfo || (subgroupContacts && subgroupContacts.length > 0), // TODO: fix this hard coded value when unit data returns call charge boolean + // TODO: fix this hard coded value when unit data returns call charge boolean + noDivider: showCallInfo + || (subgroupContacts && subgroupContacts.length > 0), }; const email = { type: 'EMAIL', @@ -50,11 +51,12 @@ const ContactInfo = ({ } : intl.formatMessage({ id: 'unit.homepage.missing' }), }; - const addNewTabSuffix = (text) => `${text} ${intl.formatMessage({ id: 'opens.new.tab' })}`; + const addNewTabSuffix = text => `${text} ${intl.formatMessage({ id: 'opens.new.tab' })}`; // Custom list item component for call charge info const callInformation = { - component: showCallInfo // TODO: fix this hard coded value when unit data returns call charge boolean + // TODO: fix this hard coded value when unit data returns call charge boolean + component: showCallInfo && ( @@ -80,44 +82,52 @@ const ContactInfo = ({ // Custom list item component for additional entrances const entrances = { component: additionalEntrances?.length - && ( - - - } - collapseContent={( - - {additionalEntrances.map(entrance => ( - entrance.name ? ( - - {getLocaleText(entrance.name)} + && ( + + + } + collapseContent={( + + {additionalEntrances.map(entrance => ( + entrance.name ? ( + + {getLocaleText(entrance.name)} + + ) : null + ))} + { + // Navigate to accessibility tab by clicking tab + if (accessiblityTabRef.current) { + accessiblityTabRef.current.click(); + accessiblityTabRef.current.focus(); + + return; + } + + // Navigate to accessibility tab by changing url tab parameter + const searchParams = parseSearchParams(location.search); + searchParams.t = 'accessibilityDetails'; + const searchString = stringifySearchParams(searchParams); + history.push(`${location.pathname}?${searchString}`); + }} + > + + {addNewTabSuffix(intl.formatMessage({ id: 'unit.entrances.accessibility' }))} - ) : null - ))} - { - // Navigate to accessibility tab by changing url tab parameter - const searchParams = parseSearchParams(location.search); - searchParams.t = 'accessibilityDetails'; - const searchString = stringifySearchParams(searchParams); - history.push(`${location.pathname}?${searchString}`); - }} - > - - {addNewTabSuffix(intl.formatMessage({ id: 'unit.entrances.accessibility' }))} - - - + + )} - /> - -
  • - -
  • - - ), + /> + +
  • + +
  • + + ), }; const otherAddress = { @@ -180,7 +190,7 @@ const ContactInfo = ({ ) : null ))} - )} + )} />
  • @@ -238,7 +248,7 @@ const ContactInfo = ({ titleComponent={headingLevel} /> ); -}; +} const StyledShortDivider = styled(Divider)(({ theme }) => ({ marginLeft: theme.spacing(9), @@ -278,6 +288,13 @@ ContactInfo.propTypes = { unit: PropTypes.objectOf(PropTypes.any).isRequired, userLocation: PropTypes.objectOf(PropTypes.any), headingLevel: PropTypes.string, + accessiblityTabRef: PropTypes.shape({ current: PropTypes.any }), +}; + +ContactInfo.defaultProps = { + userLocation: null, + headingLevel: 'h4', + accessiblityTabRef: null, }; export default ContactInfo;