diff --git a/.env.example b/.env.example index 2626f6b76..13e838a96 100644 --- a/.env.example +++ b/.env.example @@ -52,8 +52,9 @@ REITTIOPAS_URL="https://reittiopas.foli.fi/reitti/" THEME_PKG=1 MOBILITY_PLATFORM_API="https://palvelukartta-api.turku.fi" RAILWAYS_API="https://rata.digitraffic.fi/api/v1" -ROADWORKS_API="https://tie.digitraffic.fi/api/traffic-message/v1/messages" +ROADWORKS_API="https://palvelukartta-api.turku.fi/exceptional_situations/api/v1" AIR_MONITORING_API="https://palvelukartta-api.turku.fi/environment_data/api/v1" +MOBILITY_TEST_API="https://liikkumistesti-api.turku.fi/api/v1/postalcoderesult" # API URLs to fetch parking spaces data PARKING_SPACES_URL="https://parkkiopas.turku.fi/public/v1/parking_area/" diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 0ac640741..f7a7e5f91 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -18,7 +18,7 @@ on: # The branches below must be a subset of the branches above branches: [ develop ] schedule: - - cron: '29 10 * * 6' + - cron: '39 16 * * 1' jobs: analyze: @@ -38,11 +38,11 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v2 + uses: actions/checkout@v3 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v1 + uses: github/codeql-action/init@v3 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -53,7 +53,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@v1 + uses: github/codeql-action/autobuild@v3 # ℹ️ Command-line programs to run using the OS shell. # 📚 https://git.io/JvXDl @@ -67,4 +67,4 @@ jobs: # make release - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v1 + uses: github/codeql-action/analyze@v3 diff --git a/config/default.js b/config/default.js index d72e82e46..cca511771 100644 --- a/config/default.js +++ b/config/default.js @@ -285,4 +285,5 @@ export default { "airMonitoringAPI": settings.AIR_MONITORING_API, "roadworksAPI": settings.ROADWORKS_API, "railwaysAPI": settings.RAILWAYS_API, + "mobilityTestAPI": settings.MOBILITY_TEST_API, } diff --git a/package-lock.json b/package-lock.json index 32fda84c2..d6a4f52e9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "0.1.0", "dependencies": { "@datapunt/matomo-tracker-js": "^0.5.1", + "@emotion/css": "^11.11.2", "@emotion/react": "^11.10.8", "@emotion/styled": "^11.10.8", "@formatjs/intl-pluralrules": "^1.5.9", @@ -2312,6 +2313,18 @@ "stylis": "4.2.0" } }, + "node_modules/@emotion/css": { + "version": "11.11.2", + "resolved": "https://registry.npmjs.org/@emotion/css/-/css-11.11.2.tgz", + "integrity": "sha512-VJxe1ucoMYMS7DkiMdC2T7PWNbrEI0a39YRiyDvK2qq4lXwjRbVP/z4lpG+odCsRzadlR+1ywwrTzhdm5HNdew==", + "dependencies": { + "@emotion/babel-plugin": "^11.11.0", + "@emotion/cache": "^11.11.0", + "@emotion/serialize": "^1.1.2", + "@emotion/sheet": "^1.2.2", + "@emotion/utils": "^1.2.1" + } + }, "node_modules/@emotion/hash": { "version": "0.9.1", "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.1.tgz", diff --git a/package.json b/package.json index 05f703c6c..759e91263 100644 --- a/package.json +++ b/package.json @@ -4,6 +4,7 @@ "private": true, "dependencies": { "@datapunt/matomo-tracker-js": "^0.5.1", + "@emotion/css": "^11.11.2", "@emotion/react": "^11.10.8", "@emotion/styled": "^11.10.8", "@formatjs/intl-pluralrules": "^1.5.9", diff --git a/server/server.js b/server/server.js index 20d23b715..1e3d46ae0 100644 --- a/server/server.js +++ b/server/server.js @@ -273,6 +273,7 @@ const htmlTemplate = (req, reactDom, preloadedState, css, cssString, emotionCss, window.nodeEnvSettings.ROADWORKS_API = "${process.env.ROADWORKS_API}"; window.nodeEnvSettings.RAILWAYS_API = "${process.env.RAILWAYS_API}"; window.nodeEnvSettings.AIR_MONITORING_API = "${process.env.AIR_MONITORING_API}"; + window.nodeEnvSettings.MOBILITY_TEST_API = "${process.env.MOBILITY_TEST_API}"; window.nodeEnvSettings.FEATURE_SERVICEMAP_PAGE_TRACKING = "${process.env.FEATURE_SERVICEMAP_PAGE_TRACKING}"; window.appVersion = {}; diff --git a/src/components/AddressSearchBar/AddressSearchBar.js b/src/components/AddressSearchBar/AddressSearchBar.js index 0d28510b8..6ba556b73 100644 --- a/src/components/AddressSearchBar/AddressSearchBar.js +++ b/src/components/AddressSearchBar/AddressSearchBar.js @@ -14,7 +14,9 @@ import useMobileStatus from '../../utils/isMobile'; import ServiceMapAPI from '../../utils/newFetch/ServiceMapAPI'; import useLocaleText from '../../utils/useLocaleText'; import { getAddressText } from '../../utils/address'; +import { getCitySettings } from '../../redux/selectors/settings'; import { focusToPosition } from '../../views/MapView/utils/mapActions'; +import config from '../../../config'; const AddressSearchBar = ({ title, intl, handleAddressChange }) => { const getLocaleText = useLocaleText(); @@ -24,6 +26,7 @@ const AddressSearchBar = ({ title, intl, handleAddressChange }) => { const map = useSelector(state => state.mapRef); const customPosition = useSelector(state => state.user.customPosition); const position = useSelector(state => state.user.position); + const citySettings = useSelector(getCitySettings); const defaultAddress = position.addressData || customPosition.addressData; @@ -36,12 +39,14 @@ const AddressSearchBar = ({ title, intl, handleAddressChange }) => { const suggestionCount = 5; const inputRef = useRef(); - const fetchAddressResults = async (text) => { + const fetchAddressResults = async text => { const smAPI = new ServiceMapAPI(); + const municipalities = citySettings?.length ? citySettings?.join(',') : config.cities; const fetchOptions = { page_size: suggestionCount, type: 'address', address_limit: suggestionCount, + municipality: municipalities, language: locale, }; setIsFetching(true); @@ -50,7 +55,7 @@ const AddressSearchBar = ({ title, intl, handleAddressChange }) => { return results; }; - const handleAddressSelect = (address) => { + const handleAddressSelect = address => { if (!addressResults.length) return; if (inputRef.current) { inputRef.current.focus(); @@ -64,7 +69,7 @@ const AddressSearchBar = ({ title, intl, handleAddressChange }) => { focusToPosition(map, address.location?.coordinates); }; - const handleSearchBarKeyPress = (e) => { + const handleSearchBarKeyPress = e => { if (e.key === 'ArrowDown') { e.preventDefault(); if (resultIndex === null || resultIndex === addressResults.length - 1) { @@ -82,14 +87,14 @@ const AddressSearchBar = ({ title, intl, handleAddressChange }) => { } }; - const clearSuggestions = (e) => { + const clearSuggestions = e => { e.preventDefault(); setTimeout(() => { setAddressResults([]); }, 200); }; - const handleSubmit = (e) => { + const handleSubmit = e => { if (resultIndex !== null) { handleAddressSelect(addressResults[resultIndex]); } else { @@ -98,7 +103,7 @@ const AddressSearchBar = ({ title, intl, handleAddressChange }) => { clearSuggestions(e); }; - const handleInputChange = (text) => { + const handleInputChange = text => { // Reset cleared text if (cleared) { setCleared(false); @@ -108,7 +113,7 @@ const AddressSearchBar = ({ title, intl, handleAddressChange }) => { if (currentLocation) { setCurrentLocation(null); } - fetchAddressResults(text).then((data) => { + fetchAddressResults(text).then(data => { if (!isFetching) setAddressResults(data); }); } else if (addressResults.length) setAddressResults([]); diff --git a/src/components/EcoCounter/CounterMarkers/CounterMarkers.js b/src/components/EcoCounter/CounterMarkers/CounterMarkers.js index 85ec6cae5..870977f8c 100644 --- a/src/components/EcoCounter/CounterMarkers/CounterMarkers.js +++ b/src/components/EcoCounter/CounterMarkers/CounterMarkers.js @@ -1,14 +1,13 @@ import { PropTypes } from 'prop-types'; import React from 'react'; import { useSelector } from 'react-redux'; +import styled from '@emotion/styled'; import ecoCounterIcon from 'servicemap-ui-turku/assets/icons/icons-icon_ecocounter.svg'; import ecoCounterIconBw from 'servicemap-ui-turku/assets/icons/contrast/icons-icon_ecocounter-bw.svg'; import { useAccessibleMap } from '../../../redux/selectors/settings'; import { createIcon } from '../../MobilityPlatform/utils/utils'; -const CounterMarkers = ({ - classes, counterStation, children, -}) => { +const CounterMarkers = ({ counterStation, children }) => { const useContrast = useSelector(useAccessibleMap); const { Marker, Popup } = global.rL; @@ -18,22 +17,33 @@ const CounterMarkers = ({ return ( -
+ -
+ {children} -
+
-
+
); }; +const StyledWrapper = styled.div(({ theme }) => ({ + position: 'absolute', + textAlign: 'center', + marginBottom: theme.spacing(2), + width: '429px', +})); + +const StyledContentInner = styled.div(({ theme }) => ({ + borderRadius: '3px', + marginBottom: theme.spacing(1), + marginLeft: theme.spacing(1.2), + lineHeight: 1.2, + overflowX: 'hidden', +})); + CounterMarkers.propTypes = { - classes: PropTypes.shape({ - popupWrapper: PropTypes.string, - popupInner: PropTypes.string, - }).isRequired, counterStation: PropTypes.shape({ lat: PropTypes.number, lon: PropTypes.number, diff --git a/src/components/EcoCounter/CounterMarkers/index.js b/src/components/EcoCounter/CounterMarkers/index.js index 49e3af6e8..81c1fbcf9 100644 --- a/src/components/EcoCounter/CounterMarkers/index.js +++ b/src/components/EcoCounter/CounterMarkers/index.js @@ -1,5 +1,3 @@ -import { withStyles } from '@mui/styles'; import CounterMarkers from './CounterMarkers'; -import styles from './styles'; -export default withStyles(styles)(CounterMarkers); +export default CounterMarkers; diff --git a/src/components/EcoCounter/CounterMarkers/styles.js b/src/components/EcoCounter/CounterMarkers/styles.js deleted file mode 100644 index 89665557d..000000000 --- a/src/components/EcoCounter/CounterMarkers/styles.js +++ /dev/null @@ -1,17 +0,0 @@ -const styles = { - popupWrapper: { - position: 'absolute', - textAlign: 'center', - marginBottom: '20px', - width: '429px', - }, - popupInner: { - borderRadius: '3px', - marginBottom: '0.4rem', - marginLeft: '0.6rem', - lineHeight: 1.2, - overflowX: 'hidden', - }, -}; - -export default styles; diff --git a/src/components/EcoCounter/InputDate/styles.js b/src/components/EcoCounter/InputDate/styles.js deleted file mode 100644 index bb0f9e7d6..000000000 --- a/src/components/EcoCounter/InputDate/styles.js +++ /dev/null @@ -1,14 +0,0 @@ -const styles = (theme) => ({ - input: { - border: 'none', - textAlign: 'end', - paddingRight: theme.spacing(0.7), - ...theme.typography.body2, - '&:hover': { - cursor: 'pointer', - border: 'none', - }, - }, -}); - -export default styles; diff --git a/src/components/EcoCounter/TrafficCounters/EcoCounterContent/EcoCounterContent.js b/src/components/EcoCounter/TrafficCounters/EcoCounterContent/EcoCounterContent.js index 62551e386..1740eeab2 100644 --- a/src/components/EcoCounter/TrafficCounters/EcoCounterContent/EcoCounterContent.js +++ b/src/components/EcoCounter/TrafficCounters/EcoCounterContent/EcoCounterContent.js @@ -1,11 +1,13 @@ /* eslint-disable react/jsx-props-no-spreading */ /* eslint-disable react-hooks/exhaustive-deps */ -import { ButtonBase, Typography, useMediaQuery } from '@mui/material'; +import { Typography, useMediaQuery } from '@mui/material'; import PropTypes from 'prop-types'; import React, { useEffect, useState, useRef, forwardRef, } from 'react'; import { useSelector } from 'react-redux'; +import { useIntl } from 'react-intl'; +import { css } from '@emotion/css'; import DatePicker, { registerLocale } from 'react-datepicker'; import { endOfMonth, @@ -34,13 +36,24 @@ import { fetchSelectedYearData, } from '../../EcoCounterRequests/ecoCounterRequests'; import { formatDates, formatMonths } from '../../utils'; +import { + StyledButtonText, + StyledChartContainer, + StyledContentHeader, + StyledDateContainer, + StyledHeaderSubtitle, + StyledUserTypeText, + StyledStepsContainer, + StyledUserTypesContainer, + StyledButtonBase, +} from '../../styled/styled'; import LineChart from '../../LineChart'; import InputDate from '../../InputDate'; import CounterActiveText from '../CounterActiveText'; const CustomInput = forwardRef((props, ref) => ); -const EcoCounterContent = ({ classes, intl, station }) => { +const EcoCounterContent = ({ station }) => { const [ecoCounterHour, setEcoCounterHour] = useState([]); const [ecoCounterDay, setEcoCounterDay] = useState([]); const [ecoCounterWeek, setEcoCounterWeek] = useState([]); @@ -54,6 +67,7 @@ const EcoCounterContent = ({ classes, intl, station }) => { const [currentTime, setCurrentTime] = useState('hour'); const [activeStep, setActiveStep] = useState(0); + const intl = useIntl(); const locale = useSelector(state => state.user.locale); const inputRef = useRef(null); @@ -68,6 +82,18 @@ const EcoCounterContent = ({ classes, intl, station }) => { const [selectedDate, setSelectedDate] = useState(subDays(new Date(dataUntil), 1)); + const iconClass = css({ + fill: 'rgba(0, 0, 0, 255)', + width: '40px', + height: '40px', + }); + + const iconActiveClass = css({ + fill: 'rgba(255, 255, 255, 255)', + width: '40px', + height: '40px', + }); + /** When all 3 user types are rendered, a reverse order is required where 'at' is placed last */ const reverseUserTypes = () => { if (station.sensor_types.includes('at')) { @@ -144,10 +170,8 @@ const EcoCounterContent = ({ classes, intl, station }) => { * @returns JSX element */ const userTypeText = translationId => ( -
- - {intl.formatMessage({ id: translationId })} - +
+ {intl.formatMessage({ id: translationId })}
); @@ -177,16 +201,19 @@ const EcoCounterContent = ({ classes, intl, station }) => { * @returns JSX Element */ const userTypeButton = (userType, iconValue, i) => ( - setUserTypes(userType, i)} >
- +
-
+ ); /** @@ -500,11 +527,11 @@ const EcoCounterContent = ({ classes, intl, station }) => { return ( <> -
- + + {stationSource === 'TR' ? 'Telraam' : renderStationName(stationName)} - -
+ + changeDate(newDate)} @@ -516,19 +543,19 @@ const EcoCounterContent = ({ classes, intl, station }) => { maxDate={new Date(dataUntil)} customInput={} /> -
-
-
-
+ + +
+ {userTypes?.map((userType, i) => ( -
+ {renderUserTypeIcon(userType, i)} {renderUserTypeText(userType)} -
+ ))} -
+ -
+ { channel1Data={channel1Counts} channel2Data={channel2Counts} /> -
-
+ + {buttonSteps .filter(item => item.step.visible) .map((timing, i) => ( - handleClick(timing.step.type, i)} > - + {timing.step.text} - + ))} -
+
); }; EcoCounterContent.propTypes = { - classes: PropTypes.objectOf(PropTypes.string).isRequired, - intl: PropTypes.shape({ - formatMessage: PropTypes.func, - }).isRequired, station: PropTypes.shape({ id: PropTypes.number, name: PropTypes.string, diff --git a/src/components/EcoCounter/TrafficCounters/EcoCounterContent/index.js b/src/components/EcoCounter/TrafficCounters/EcoCounterContent/index.js index e0ddb831f..9f51169c7 100644 --- a/src/components/EcoCounter/TrafficCounters/EcoCounterContent/index.js +++ b/src/components/EcoCounter/TrafficCounters/EcoCounterContent/index.js @@ -1,6 +1,3 @@ -import { withStyles } from '@mui/styles'; -import { injectIntl } from 'react-intl'; import EcoCounterContent from './EcoCounterContent'; -import styles from '../styles'; -export default withStyles(styles)(injectIntl(EcoCounterContent)); +export default EcoCounterContent; diff --git a/src/components/EcoCounter/TrafficCounters/LamCounterContent/LamCounterContent.js b/src/components/EcoCounter/TrafficCounters/LamCounterContent/LamCounterContent.js index 91568901b..9b33a56ea 100644 --- a/src/components/EcoCounter/TrafficCounters/LamCounterContent/LamCounterContent.js +++ b/src/components/EcoCounter/TrafficCounters/LamCounterContent/LamCounterContent.js @@ -4,8 +4,11 @@ import React, { useEffect, useState, forwardRef, useRef, } from 'react'; import { useSelector } from 'react-redux'; -import { ButtonBase, Typography, useMediaQuery } from '@mui/material'; +import { Typography, useMediaQuery } from '@mui/material'; import PropTypes from 'prop-types'; +import { useIntl } from 'react-intl'; +import { css } from '@emotion/css'; +import styled from '@emotion/styled'; import DatePicker, { registerLocale } from 'react-datepicker'; import { endOfMonth, @@ -34,10 +37,21 @@ import { formatDates, formatMonths } from '../../utils'; import LineChart from '../../LineChart'; import InputDate from '../../InputDate'; import CounterActiveText from '../CounterActiveText'; +import { + StyledButtonBase, + StyledChartContainer, + StyledContentHeader, + StyledDateContainer, + StyledHeaderSubtitle, + StyledIconContainer, + StyledStepsContainer, + StyledUserTypeText, + StyledUserTypesContainer, +} from '../../styled/styled'; const CustomInput = forwardRef((props, ref) => ); -const LamCounterContent = ({ classes, intl, station }) => { +const LamCounterContent = ({ station }) => { const [lamCounterHour, setLamCounterHour] = useState([]); const [lamCounterDay, setLamCounterDay] = useState([]); const [lamCounterWeek, setLamCounterWeek] = useState([]); @@ -51,6 +65,7 @@ const LamCounterContent = ({ classes, intl, station }) => { const [currentTime, setCurrentTime] = useState('hour'); const [activeStep, setActiveStep] = useState(0); + const intl = useIntl(); const locale = useSelector(state => state.user.locale); const inputRef = useRef(null); @@ -66,6 +81,12 @@ const LamCounterContent = ({ classes, intl, station }) => { const [selectedDate, setSelectedDate] = useState(subDays(new Date(dataUntil), 1)); + const iconClass = css({ + fill: 'rgb(255, 255, 255)', + width: '40px', + height: '40px', + }); + // steps that determine which data is shown on the chart const buttonSteps = [ { @@ -103,10 +124,10 @@ const LamCounterContent = ({ classes, intl, station }) => { const renderUserTypeText = userType => { if (userType === 'at') { return ( -
- +
+ {intl.formatMessage({ id: 'ecocounter.car' })} - +
); } @@ -116,9 +137,9 @@ const LamCounterContent = ({ classes, intl, station }) => { const renderUserTypeIcon = userType => { if (userType === 'at') { return ( -
- -
+ + + ); } return null; @@ -383,11 +404,11 @@ const LamCounterContent = ({ classes, intl, station }) => { return ( <> -
- + + {stationSource === 'LC' ? formatCounterName(stationName) : stationName} - -
+ + changeDate(newDate)} @@ -399,26 +420,26 @@ const LamCounterContent = ({ classes, intl, station }) => { maxDate={new Date(dataUntil)} customInput={} /> -
-
-
-
+ + +
+ {userTypes?.map(userType => ( -
+
{renderUserTypeIcon(userType)} {renderUserTypeText(userType)}
))} -
+
{lamCounterYear?.value_at === 0 ? ( -
+ {intl.formatMessage({ id: 'trafficCounter.year.warning.text' }, { value: selectedYear })} -
+ ) : null} -
+ { channel1Data={channel1Counts} channel2Data={channel2Counts} /> -
-
+ + {buttonSteps.map((timing, i) => ( - handleClick(timing.step.type, i)} > - + {timing.step.text} - + ))} -
+
); }; +const StyledMissingDataText = styled.div(({ theme }) => ({ + textAlign: 'center', + margin: `${theme.spacing(1)} 0`, +})); + LamCounterContent.propTypes = { - classes: PropTypes.objectOf(PropTypes.string).isRequired, - intl: PropTypes.shape({ - formatMessage: PropTypes.func, - }).isRequired, station: PropTypes.shape({ id: PropTypes.number, name: PropTypes.string, diff --git a/src/components/EcoCounter/TrafficCounters/LamCounterContent/index.js b/src/components/EcoCounter/TrafficCounters/LamCounterContent/index.js index 978bfbb83..e5dc3441f 100644 --- a/src/components/EcoCounter/TrafficCounters/LamCounterContent/index.js +++ b/src/components/EcoCounter/TrafficCounters/LamCounterContent/index.js @@ -1,6 +1,3 @@ -import { withStyles } from '@mui/styles'; -import { injectIntl } from 'react-intl'; import LamCounterContent from './LamCounterContent'; -import styles from '../styles'; -export default withStyles(styles)(injectIntl(LamCounterContent)); +export default LamCounterContent; diff --git a/src/components/EcoCounter/TrafficCounters/styles.js b/src/components/EcoCounter/TrafficCounters/styles.js deleted file mode 100644 index b90904946..000000000 --- a/src/components/EcoCounter/TrafficCounters/styles.js +++ /dev/null @@ -1,122 +0,0 @@ -const styles = (theme) => ({ - buttonTransparent: { - backgroundColor: '#fff', - border: 'none', - cursor: 'pointer', - '&:hover': { - color: 'rgba(84, 84, 84, 255)', - }, - }, - button: { - border: '1px solid gray', - borderRadius: '5px', - cursor: 'pointer', - marginRight: theme.spacing(1.5), - }, - buttonGray: { - backgroundColor: '#ddd', - border: 'none', - borderRadius: '5px', - color: '#fff', - cursor: 'pointer', - padding: theme.spacing(1), - }, - buttonWhite: { - backgroundColor: '#fff', - }, - buttonActive: { - backgroundColor: 'rgba(7, 44, 115, 255)', - color: '#fff', - }, - paddingWide: { - padding: `${theme.spacing(0.5)} ${theme.spacing(1)}`, - }, - paddingNarrow: { - padding: theme.spacing(0.5), - }, - widthMd: { - width: '95%', - }, - widthSm: { - width: '87%', - }, - trafficCounterHeader: { - display: 'flex', - flexDirection: 'row', - marginTop: theme.spacing(0.5), - marginBottom: theme.spacing(1.5), - alignItems: 'flex-end', - borderBottom: '2px solid gray', - justifyContent: 'space-between', - }, - headerSubtitle: { - padding: '4px 0 5px', - fontWeight: 'bold', - marginBlockStart: theme.spacing(2), - marginBlockEnd: theme.spacing(0.2), - }, - dateContainer: { - display: 'flex', - flexDirection: 'row', - alignItems: 'center', - maxWidth: '32%', - }, - trafficCounterUserTypes: { - display: 'flex', - justifyContent: 'flex-end', - marginBottom: theme.spacing(0.5), - }, - buttonAndTextContainer: { - display: 'flex', - flexDirection: 'column', - marginRight: '0.7rem', - alignItems: 'center', - }, - userTypeText: { - fontWeight: 'bold', - paddingTop: theme.spacing(0.5), - paddingRight: theme.spacing(1), - fontSize: '0.8rem', - }, - buttonText: { - fontSize: '0.75rem', - }, - missingDataText: { - textAlign: 'center', - margin: `${theme.spacing(1)} 0`, - }, - iconWrapper: { - backgroundColor: 'rgba(7, 44, 115, 255)', - color: '#fff', - border: '1px solid gray', - borderRadius: '5px', - marginRight: theme.spacing(1.5), - padding: theme.spacing(0.5), - }, - iconActive: { - fill: '#ffffff', - width: '40px', - height: '40px', - }, - icon: { - fill: '#000000', - width: '40px', - height: '40px', - }, - trafficCounterSteps: { - display: 'flex', - flexDirection: 'row', - justifyContent: 'space-evenly', - alignItems: 'center', - padding: '1rem 0', - }, - trafficCounterChart: { - margin: 0, - }, - iconContainer: { - marginRight: theme.spacing(0.5), - paddingTop: theme.spacing(1), - }, -}); - -export default styles; diff --git a/src/components/EcoCounter/styled/styled.js b/src/components/EcoCounter/styled/styled.js new file mode 100644 index 000000000..88ad3fc66 --- /dev/null +++ b/src/components/EcoCounter/styled/styled.js @@ -0,0 +1,88 @@ +import styled from '@emotion/styled'; +import { ButtonBase, Typography } from '@mui/material'; + +const StyledContentHeader = styled.div(({ theme, isNarrow }) => ({ + display: 'flex', + flexDirection: 'row', + marginTop: theme.spacing(0.5), + marginBottom: theme.spacing(1.5), + alignItems: 'flex-end', + borderBottom: '2px solid gray', + justifyContent: 'space-between', + width: isNarrow ? '87%' : '95%', +})); + +const StyledHeaderSubtitle = styled(Typography)(({ theme }) => ({ + padding: '4px 0 5px', + fontWeight: 'bold', + marginBlockStart: theme.spacing(2), + marginBlockEnd: theme.spacing(0.2), +})); + +const StyledUserTypeText = styled(Typography)(({ theme }) => ({ + fontWeight: 'bold', + paddingTop: theme.spacing(0.5), + paddingRight: theme.spacing(1), + fontSize: '0.8rem', +})); + +const StyledButtonBase = styled(ButtonBase)(({ theme }) => ({ + border: '1px solid gray', + borderRadius: '5px', + cursor: 'pointer', + marginRight: theme.spacing(1.5), +})); + +const StyledDateContainer = styled.div(() => ({ + display: 'flex', + flexDirection: 'row', + alignItems: 'center', + maxWidth: '32%', +})); + +const StyledUserTypesContainer = styled.div(({ theme }) => ({ + display: 'flex', + justifyContent: 'flex-end', + marginBottom: theme.spacing(0.5), +})); + +const StyledButtonText = styled.div(() => ({ + display: 'flex', + flexDirection: 'column', + marginRight: '0.7rem', + alignItems: 'center', +})); + +const StyledChartContainer = styled.div(() => ({ + margin: 0, +})); + +const StyledStepsContainer = styled.div(() => ({ + display: 'flex', + flexDirection: 'row', + justifyContent: 'space-evenly', + alignItems: 'center', + padding: '1rem 0', +})); + +const StyledIconContainer = styled.div(({ theme }) => ({ + backgroundColor: 'rgba(7, 44, 115, 255)', + color: 'rgba(255, 255, 255, 255)', + border: '1px solid gray', + borderRadius: '5px', + marginRight: theme.spacing(1.5), + padding: theme.spacing(0.5), +})); + +export { + StyledContentHeader, + StyledHeaderSubtitle, + StyledUserTypeText, + StyledButtonBase, + StyledDateContainer, + StyledUserTypesContainer, + StyledButtonText, + StyledChartContainer, + StyledStepsContainer, + StyledIconContainer, +}; diff --git a/src/components/ListItems/AddressItem/__tests__/__snapshots__/AddressItem.test.js.snap b/src/components/ListItems/AddressItem/__tests__/__snapshots__/AddressItem.test.js.snap index 8be57bd3f..a1d438a18 100644 --- a/src/components/ListItems/AddressItem/__tests__/__snapshots__/AddressItem.test.js.snap +++ b/src/components/ListItems/AddressItem/__tests__/__snapshots__/AddressItem.test.js.snap @@ -3,21 +3,22 @@ exports[` should work 1`] = `
  • should work 1`] = ` aria-hidden="true" >
  • diff --git a/src/components/ListItems/ServiceItem/__tests__/__snapshots__/ServiceItem.test.js.snap b/src/components/ListItems/ServiceItem/__tests__/__snapshots__/ServiceItem.test.js.snap index 8760fb63c..0fba4c124 100644 --- a/src/components/ListItems/ServiceItem/__tests__/__snapshots__/ServiceItem.test.js.snap +++ b/src/components/ListItems/ServiceItem/__tests__/__snapshots__/ServiceItem.test.js.snap @@ -3,13 +3,14 @@ exports[` should work 1`] = `
  • should work 1`] = ` aria-hidden="true" >
  • diff --git a/src/components/ListItems/SimpleListItem/SimpleListItem.js b/src/components/ListItems/SimpleListItem/SimpleListItem.js index 377bd7dfb..5cdf10f8b 100644 --- a/src/components/ListItems/SimpleListItem/SimpleListItem.js +++ b/src/components/ListItems/SimpleListItem/SimpleListItem.js @@ -1,31 +1,56 @@ import PropTypes from 'prop-types'; import React from 'react'; import { Divider, Typography } from '@mui/material'; +import { useTheme } from '@mui/styles'; +import { css } from '@emotion/css'; +import styled from '@emotion/styled'; import ListItem from '@mui/material/ListItem'; import ListItemIcon from '@mui/material/ListItemIcon'; import ListItemText from '@mui/material/ListItemText'; import { visuallyHidden } from '@mui/utils'; import { keyboardHandler } from '../../../utils'; -const SimpleListItem = (props) => { +const SimpleListItem = props => { const { - button, - dark, - text, - classes, - link, - icon, - handleItemClick, - role, - divider, - selected, - srText, - className, - id, + button, dark, text, link, icon, handleItemClick, role, divider, selected, srText, className, id, } = props; + const isLinkOrButton = button || link; + const theme = useTheme(); + + const listItemRootClass = css({ + minHeight: '3.5rem', + padding: theme.spacing(0), + color: '#000', + '&.dark': { + paddingLeft: 26, + color: '#fff', + }, + }); + + const listItemSelectedClass = css({ + outline: '2px solid transparent', + boxShadow: `0 0 0 4px ${theme.palette.focusBorder.main}`, + }); + + const listItemTextClass = css({ + padding: theme.spacing(1, 0), + marginLeft: theme.spacing(2), + marginRight: theme.spacing(2), + whiteSpace: 'pre-line', + }); + + const linkClass = css({ + color: theme.palette.link.main, + textDecoration: 'underline', + }); + + const whiteTextClass = css({ + color: '#fff', + }); + return ( - + <> { onClick={isLinkOrButton ? handleItemClick : null} onKeyDown={isLinkOrButton ? keyboardHandler(handleItemClick, ['enter', 'space']) : null} classes={{ - root: classes.listItem, - selected: classes.itemFocus, + root: listItemRootClass, + selected: listItemSelectedClass, }} selected={selected} id={id} > - { - icon - && ( - - {icon} - - ) - } + {icon && ( + + {icon} + + )} - + {text} @@ -65,18 +85,40 @@ const SimpleListItem = (props) => { {divider && (
  • - +
  • )} -
    + ); }; +const StyledListItemIcon = styled(ListItemIcon)(({ theme, link }) => { + const styles = { + width: '1.5rem', + height: '1.5rem', + margin: theme.spacing(1), + marginRight: theme.spacing(2), + minWidth: 0, + color: 'inherit', + }; + if (link) { + Object.assign(styles, { + color: theme.palette.link.main, + textDecoration: 'underline', + }); + } + return styles; +}); + +const StyledDivider = styled(Divider)(({ theme }) => ({ + marginLeft: theme.spacing(9), + marginRight: theme.spacing(-2), +})); + export default SimpleListItem; SimpleListItem.propTypes = { button: PropTypes.bool, - classes: PropTypes.objectOf(PropTypes.any).isRequired, dark: PropTypes.bool, text: PropTypes.string.isRequired, srText: PropTypes.string, diff --git a/src/components/ListItems/SimpleListItem/__tests__/__snapshots__/SimpleListItem.test.js.snap b/src/components/ListItems/SimpleListItem/__tests__/__snapshots__/SimpleListItem.test.js.snap index 08696eb8a..9a120cbae 100644 --- a/src/components/ListItems/SimpleListItem/__tests__/__snapshots__/SimpleListItem.test.js.snap +++ b/src/components/ListItems/SimpleListItem/__tests__/__snapshots__/SimpleListItem.test.js.snap @@ -3,11 +3,11 @@ exports[` should work 1`] = `
  • ({ - listItem: { - minHeight: '3.5rem', - padding: theme.spacing(1), - color: '#000', - '&.dark': { - paddingLeft: 26, - color: '#fff', - }, - }, - textContainer: { - padding: theme.spacing(1, 0), - marginLeft: theme.spacing(2), - marginRight: theme.spacing(2), - whiteSpace: 'pre-line', - }, - link: { - color: theme.palette.link.main, - textDecoration: 'underline', - }, - whiteText: { - color: '#fff', - }, - listIcon: { - width: '1.5rem', - height: '1.5rem', - margin: theme.spacing(1), - marginRight: theme.spacing(2), - minWidth: 0, - color: 'inherit', - }, - divider: { - marginLeft: theme.spacing(9), - marginRight: theme.spacing(-2), - }, - itemFocus: { - outline: '2px solid transparent', - boxShadow: `0 0 0 4px ${theme.palette.focusBorder.main}`, - }, -}); diff --git a/src/components/MobilityPlatform/AccessibilityAreas/AccessibilityAreas.js b/src/components/MobilityPlatform/AccessibilityAreas/AccessibilityAreas.js new file mode 100644 index 000000000..43736b41c --- /dev/null +++ b/src/components/MobilityPlatform/AccessibilityAreas/AccessibilityAreas.js @@ -0,0 +1,175 @@ +/* eslint-disable react-hooks/exhaustive-deps */ +import React, { useEffect } from 'react'; +import { useSelector } from 'react-redux'; +import { useMap } from 'react-leaflet'; +import walkingIcon from 'servicemap-ui-turku/assets/icons/icons-icon_walking_area.svg'; +import walkingIconBw from 'servicemap-ui-turku/assets/icons/contrast/icons-icon_walking_area-bw.svg'; +import cyclingIcon from 'servicemap-ui-turku/assets/icons/icons-icon_cycling_area.svg'; +import cyclingIconBw from 'servicemap-ui-turku/assets/icons/contrast/icons-icon_cycling_area-bw.svg'; +import { useMobilityPlatformContext } from '../../../context/MobilityPlatformContext'; +import { useAccessibleMap } from '../../../redux/selectors/settings'; +import { + isDataValid, + createIcon, + fitPolygonsToBounds, + setRender, + blueOptionsBase, + whiteOptionsBase, + greenOptionsBase, + blackOptionsBase, +} from '../utils/utils'; +import { isEmbed } from '../../../utils/path'; +import { StyledPopupWrapper, StyledPopupInner } from '../styled/styled'; +import AccessibilityAreasContent from './components/AccessibilityAreasContent'; + +/** + * Displays school accessibility areas on the map in polygon format. + */ +const AccessibilityAreas = () => { + const { showAccessibilityAreas, accessibilityAreasData } = useMobilityPlatformContext(); + + const selectedUnit = useSelector(state => state.selectedUnit?.unit?.data); + const unitId = selectedUnit?.id; + const useContrast = useSelector(useAccessibleMap); + + const url = new URL(window.location); + const embedded = isEmbed({ url: url.toString() }); + + const map = useMap(); + const { Marker, Polygon, Popup } = global.rL; + const { icon } = global.L; + + const walk = 'kävely'; + const bicycle = 'pyöräily'; + + const blueOptions = blueOptionsBase({ weight: 5, dashArray: '12 6 3', fillOpacity: '0' }); + const greenOptions = greenOptionsBase({ weight: 5, fillOpacity: '0' }); + const blackOptions = blackOptionsBase({ weight: 5 }); + const whiteOptionsDashed = whiteOptionsBase({ + fillOpacity: '0.1', + weight: 5, + dashArray: '12 6 3', + }); + const whiteOptionsSolid = whiteOptionsBase({ + fillOpacity: '0.1', + weight: 5, + }); + + const getPathOptions = transportType => { + const isWalk = transportType.includes(walk); + const isBicycle = transportType.includes(bicycle); + if (!useContrast && isWalk) { + return blueOptions; + } + if (!useContrast && isBicycle) { + return greenOptions; + } + if (useContrast && isWalk) { + return whiteOptionsDashed; + } + if (useContrast && isBicycle) { + return whiteOptionsSolid; + } + return blackOptions; + }; + + const walkingAreaIcon = icon(createIcon(useContrast ? walkingIconBw : walkingIcon, true)); + const cyclingAreaIcon = icon(createIcon(useContrast ? cyclingIconBw : cyclingIcon, true)); + + const getCorrectIcon = transportType => { + if (transportType?.includes(walk)) { + return walkingAreaIcon; + } + return cyclingAreaIcon; + }; + + const paramValue = url.searchParams.get('accessibility_areas') === '1'; + const paramValueWalk = url.searchParams.get('accessibility_areas_walk') === '1'; + const paramValueBicycle = url.searchParams.get('accessibility_areas_bicycle') === '1'; + const filteredAreasWalking = accessibilityAreasData.filter( + item => item.extra?.kohde_ID === unitId && item?.extra?.kulkumuoto?.includes(walk), + ); + const filteredAreasCycling = accessibilityAreasData.filter( + item => item.extra?.kohde_ID === unitId && item?.extra?.kulkumuoto?.includes(bicycle), + ); + const renderAll = setRender(paramValue, embedded, showAccessibilityAreas.all, accessibilityAreasData, isDataValid); + const renderWalking = setRender( + paramValueWalk, + embedded, + showAccessibilityAreas.walking, + filteredAreasWalking, + isDataValid, + ); + const renderCycling = setRender( + paramValueBicycle, + embedded, + showAccessibilityAreas.cycling, + filteredAreasCycling, + isDataValid, + ); + + useEffect(() => { + if (!embedded) { + fitPolygonsToBounds(renderAll, accessibilityAreasData, map); + } + }, [showAccessibilityAreas.all, accessibilityAreasData]); + + useEffect(() => { + if (!embedded) { + fitPolygonsToBounds(renderWalking, filteredAreasWalking, map); + } + }, [showAccessibilityAreas.walking, filteredAreasWalking]); + + useEffect(() => { + if (!embedded) { + fitPolygonsToBounds(renderCycling, filteredAreasCycling, map); + } + }, [showAccessibilityAreas.cycling, filteredAreasCycling]); + + const getSingleCoordinates = data => data[0][0]; + + const renderMarkers = (showData, data) => (showData + ? data.map(item => ( + + + + + + + + + + )) + : null); + + const renderPolygons = (showData, data) => (showData + ? data.map(item => ( + + + + + + + + + + )) + : null); + + return ( + <> + {renderMarkers(renderAll, accessibilityAreasData)} + {renderMarkers(renderWalking, filteredAreasWalking)} + {renderMarkers(renderCycling, filteredAreasCycling)} + {renderPolygons(renderAll, accessibilityAreasData)} + {renderPolygons(renderWalking, filteredAreasWalking)} + {renderPolygons(renderCycling, filteredAreasCycling)} + + ); +}; + +export default AccessibilityAreas; diff --git a/src/components/MobilityPlatform/AccessibilityAreas/components/AccessibilityAreasContent/AccessibilityAreasContent.js b/src/components/MobilityPlatform/AccessibilityAreas/components/AccessibilityAreasContent/AccessibilityAreasContent.js new file mode 100644 index 000000000..d6dd5e066 --- /dev/null +++ b/src/components/MobilityPlatform/AccessibilityAreas/components/AccessibilityAreasContent/AccessibilityAreasContent.js @@ -0,0 +1,54 @@ +import PropTypes from 'prop-types'; +import React from 'react'; +import { Typography } from '@mui/material'; +import { useIntl } from 'react-intl'; +import { StyledContainer, StyledHeaderContainer, StyledTextContainer } from '../../../styled/styled'; + +const AccessibilityAreasContent = ({ item }) => { + const intl = useIntl(); + + const contentInfo = ( + + + + {item.name_fi} + + +
    + + + {intl.formatMessage({ id: 'unit.accessibilityAreas.content.subtitle' })} + + + + + {intl.formatMessage({ id: 'unit.accessibilityAreas.content.transport' }, { value: item?.extra?.kulkumuoto })} + + + + + {intl.formatMessage({ id: 'unit.accessibilityAreas.content.duration' }, { value: item?.extra?.minuutit })} + + +
    +
    + ); + + return
    {contentInfo}
    ; +}; + +AccessibilityAreasContent.propTypes = { + item: PropTypes.shape({ + name_fi: PropTypes.string, + extra: PropTypes.shape({ + kulkumuoto: PropTypes.string, + minuutit: PropTypes.number, + }), + }), +}; + +AccessibilityAreasContent.defaultProps = { + item: {}, +}; + +export default AccessibilityAreasContent; diff --git a/src/components/MobilityPlatform/AccessibilityAreas/components/AccessibilityAreasContent/__tests__/AccessibilityAreasContent.test.js b/src/components/MobilityPlatform/AccessibilityAreas/components/AccessibilityAreasContent/__tests__/AccessibilityAreasContent.test.js new file mode 100644 index 000000000..dec0feccc --- /dev/null +++ b/src/components/MobilityPlatform/AccessibilityAreas/components/AccessibilityAreasContent/__tests__/AccessibilityAreasContent.test.js @@ -0,0 +1,42 @@ +// Link.react.test.js +import React from 'react'; +import AccessibilityAreasContent from '../index'; +import { getRenderWithProviders } from '../../../../../../../jestUtils'; +import finnishTranslations from '../../../../../../i18n/fi'; + +const mockProps = { + item: { + extra: { + kulkumuoto: 'Kävely', + minuutit: 10, + }, + }, +}; + +const renderWithProviders = getRenderWithProviders({}); + +describe('', () => { + it('should work', () => { + const { container } = renderWithProviders(); + expect(container).toMatchSnapshot(); + }); + + it('does show text correctly', () => { + const { container } = renderWithProviders(); + + const p = container.querySelectorAll('p'); + expect(p[0].textContent).toContain(`${finnishTranslations['unit.accessibilityAreas.content.subtitle']}`); + expect(p[1].textContent).toContain( + `${finnishTranslations['unit.accessibilityAreas.content.transport'].replace( + '{value}', + `${mockProps.item.extra.kulkumuoto}`, + )}`, + ); + expect(p[2].textContent).toContain( + `${finnishTranslations['unit.accessibilityAreas.content.duration'].replace( + '{value}', + `${mockProps.item.extra.minuutit}`, + )}`, + ); + }); +}); diff --git a/src/components/MobilityPlatform/AccessibilityAreas/components/AccessibilityAreasContent/__tests__/__snapshots__/AccessibilityAreasContent.test.js.snap b/src/components/MobilityPlatform/AccessibilityAreas/components/AccessibilityAreasContent/__tests__/__snapshots__/AccessibilityAreasContent.test.js.snap new file mode 100644 index 000000000..0b629c5ee --- /dev/null +++ b/src/components/MobilityPlatform/AccessibilityAreas/components/AccessibilityAreasContent/__tests__/__snapshots__/AccessibilityAreasContent.test.js.snap @@ -0,0 +1,48 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[` should work 1`] = ` +
    +
    +
    +
    +

    +

    +
    +
    +

    + Lähestymisalue: +

    +
    +
    +

    + Kulkumuto: Kävely +

    +
    +
    +

    + Arvioitu aika: 10 minuuttia +

    +
    +
    +
    +
    +
    +`; diff --git a/src/components/MobilityPlatform/AccessibilityAreas/components/AccessibilityAreasContent/index.js b/src/components/MobilityPlatform/AccessibilityAreas/components/AccessibilityAreasContent/index.js new file mode 100644 index 000000000..2d5f73b4b --- /dev/null +++ b/src/components/MobilityPlatform/AccessibilityAreas/components/AccessibilityAreasContent/index.js @@ -0,0 +1,3 @@ +import AccessibilityAreasContent from './AccessibilityAreasContent'; + +export default AccessibilityAreasContent; diff --git a/src/components/MobilityPlatform/AccessibilityAreas/index.js b/src/components/MobilityPlatform/AccessibilityAreas/index.js new file mode 100644 index 000000000..7ec4f14f7 --- /dev/null +++ b/src/components/MobilityPlatform/AccessibilityAreas/index.js @@ -0,0 +1,3 @@ +import AccessibilityAreas from './AccessibilityAreas'; + +export default AccessibilityAreas; diff --git a/src/components/MobilityPlatform/AirportFlights/AirportFlights.js b/src/components/MobilityPlatform/AirportFlights/AirportFlights.js new file mode 100644 index 000000000..2e0228b8b --- /dev/null +++ b/src/components/MobilityPlatform/AirportFlights/AirportFlights.js @@ -0,0 +1,56 @@ +/* eslint-disable react-hooks/exhaustive-deps */ +import React, { useEffect } from 'react'; +import { useMap } from 'react-leaflet'; +import { useSelector } from 'react-redux'; +import airPlaneIconBw from 'servicemap-ui-turku/assets/icons/contrast/icons-icon_airplane-bw.svg'; +import airPlaneIcon from 'servicemap-ui-turku/assets/icons/icons-icon_airplane.svg'; +import { useMobilityPlatformContext } from '../../../context/MobilityPlatformContext'; +import { useAccessibleMap } from '../../../redux/selectors/settings'; +import { createIcon } from '../utils/utils'; +import useIotDataFetch from '../utils/useIotDataFetch'; +import { StyledPopupWrapper, StyledPopupInner } from '../styled/styled'; +import AirportFlightsContent from './components/AirportFlightsContent'; + +const AirportFlights = () => { + const { showAirports } = useMobilityPlatformContext(); + + const map = useMap(); + + const useContrast = useSelector(useAccessibleMap); + + const { Marker, Popup } = global.rL; + const { icon } = global.L; + + const customIcon = icon(createIcon(useContrast ? airPlaneIconBw : airPlaneIcon)); + + const airportData = { + id: 'airports', + lat: 60.514722, + lon: 22.261667, + }; + + useEffect(() => { + if (showAirports) { + const bounds = []; + bounds.push([airportData.lat, airportData.lon]); + map.fitBounds(bounds); + } + }, [showAirports]); + + const { iotData: arrivalsData } = useIotDataFetch('FAA', showAirports); + const { iotData: departeesData } = useIotDataFetch('FAD', showAirports); + + return showAirports ? ( + + + + + + + + + + ) : null; +}; + +export default AirportFlights; diff --git a/src/components/MobilityPlatform/AirportFlights/components/AirportFlightsContent/AirportFlightsContent.js b/src/components/MobilityPlatform/AirportFlights/components/AirportFlightsContent/AirportFlightsContent.js new file mode 100644 index 000000000..d22679ad0 --- /dev/null +++ b/src/components/MobilityPlatform/AirportFlights/components/AirportFlightsContent/AirportFlightsContent.js @@ -0,0 +1,141 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { useIntl } from 'react-intl'; +import { Typography } from '@mui/material'; +import { css } from '@emotion/css'; +import { format } from 'date-fns'; +import { ReactSVG } from 'react-svg'; +import planeIcon from 'servicemap-ui-turku/assets/icons/icons-icon_airplane-square.svg'; +import { + StyledContainer, StyledHeaderContainer, StyledFlexContainer, StyledTextContainer, +} from '../../../styled/styled'; + +const AirportFlightsContent = ({ arrivals, departees }) => { + const intl = useIntl(); + + const iconClass = css({ + fill: 'rgba(7, 44, 115, 255)', + width: '18px', + height: '18px', + }); + + const currentDate = new Date(); + const formatDate = dateValue => format(new Date(dateValue), 'MM-dd'); + const formatDateTime = dateTimeValue => format(new Date(dateTimeValue), 'dd.MM HH:mm'); + + const filterArrivals = arrivals.filter(item => item.sdt.includes(formatDate(currentDate))); + const filterDepartees = departees.filter(item => item.sdt.includes(formatDate(currentDate))); + + const getLocalizedCities = cityStr => { + switch (cityStr) { + case 'Stockholm': + return intl.formatMessage({ id: 'mobilityPlatform.content.airport.cities.stockholm' }); + case 'Mariehamn': + return intl.formatMessage({ id: 'mobilityPlatform.content.airport.cities.mariehamn' }); + case 'Riga': + return intl.formatMessage({ id: 'mobilityPlatform.content.airport.cities.riga' }); + case 'Gdansk': + return intl.formatMessage({ id: 'mobilityPlatform.content.airport.cities.gdansk' }); + case 'Rome': + return intl.formatMessage({ id: 'mobilityPlatform.content.airport.cities.rome' }); + case 'Rhodes': + return intl.formatMessage({ id: 'mobilityPlatform.content.airport.cities.rhodes' }); + default: + return cityStr; + } + }; + + const renderText = (translationId, isTitle) => ( + + + {intl.formatMessage({ id: translationId })} + + + ); + + return ( + + + + {intl.formatMessage({ id: 'mobilityPlatform.content.airport.title' })} + + +
    + {renderText('mobilityPlatform.content.airport.departees', true)} + {!filterDepartees?.length ? ( + renderText('mobilityPlatform.content.airport.departees.empty') + ) : null} + {filterDepartees?.map(item => ( + + + + + + + {item.fltnr} + + + + + {getLocalizedCities(item.route_n_1)} + + + + + {formatDateTime(item.sdt)} + + + + ))} +
    +
    + {renderText('mobilityPlatform.content.airport.arrivals', true)} + {!filterArrivals?.length ? ( + renderText('mobilityPlatform.content.airport.arrivals.empty') + ) : null} + {filterArrivals?.map(item => ( + + + + + + + {item.fltnr} + + + + + {intl.formatMessage({ id: 'mobilityPlatform.content.airport.cities.turku' })} + + + + + {formatDateTime(item.sdt)} + + + + ))} +
    +
    + ); +}; + +AirportFlightsContent.propTypes = { + arrivals: PropTypes.arrayOf(PropTypes.shape({ + sdt: PropTypes.string, + fltnr: PropTypes.string, + route_n_1: PropTypes.string, + })), + departees: PropTypes.arrayOf(PropTypes.shape({ + sdt: PropTypes.string, + fltnr: PropTypes.string, + route_n_1: PropTypes.string, + })), +}; + +AirportFlightsContent.defaultProps = { + arrivals: [], + departees: [], +}; + +export default AirportFlightsContent; diff --git a/src/components/MobilityPlatform/AirportFlights/components/AirportFlightsContent/__tests__/AirportFlightsContent.test.js b/src/components/MobilityPlatform/AirportFlights/components/AirportFlightsContent/__tests__/AirportFlightsContent.test.js new file mode 100644 index 000000000..a55aba84b --- /dev/null +++ b/src/components/MobilityPlatform/AirportFlights/components/AirportFlightsContent/__tests__/AirportFlightsContent.test.js @@ -0,0 +1,60 @@ +// Link.react.test.js +import React from 'react'; +import { getRenderWithProviders } from '../../../../../../../jestUtils'; +import finnishTranslations from '../../../../../../i18n/fi'; +import AirportFlightsContent from '../index'; + +const mockProps = { + departees: [ + { + sdt: '2024-04-26T11:00:00Z', + fltnr: 'TST1', + route_n_1: 'Stockholm', + }, + { + sdt: '2024-04-26T12:00:00Z', + fltnr: 'TST2', + route_n_1: 'Riga', + }, + { + sdt: '2024-04-26T17:00:00Z', + fltnr: 'TST3', + route_n_1: 'Rome', + }, + ], + arrvals: [ + { + sdt: '2024-04-26T10:00:00Z', + fltnr: 'TST1', + route_n_1: 'Turku', + }, + { + sdt: '2024-04-26T15:00:00Z', + fltnr: 'TST2', + route_n_1: 'Turku', + }, + { + sdt: '2024-04-26T20:00:00Z', + fltnr: 'TST3', + route_n_1: 'Turku', + }, + ], +}; + +const renderWithProviders = getRenderWithProviders({}); + +describe('', () => { + it('should match snapshot', () => { + const { container } = renderWithProviders(); + expect(container).toMatchSnapshot(); + }); + + it('does show text', () => { + const { container } = renderWithProviders(); + const p = container.querySelectorAll('p'); + const h4 = container.querySelector('h4'); + expect(h4.textContent).toContain(finnishTranslations['mobilityPlatform.content.airport.title']); + expect(p[0].textContent).toContain(finnishTranslations['mobilityPlatform.content.airport.departees']); + expect(p[1]).toBeInTheDocument(); + }); +}); diff --git a/src/components/MobilityPlatform/AirportFlights/components/AirportFlightsContent/__tests__/__snapshots__/AirportFlightsContent.test.js.snap b/src/components/MobilityPlatform/AirportFlights/components/AirportFlightsContent/__tests__/__snapshots__/AirportFlightsContent.test.js.snap new file mode 100644 index 000000000..4360efe16 --- /dev/null +++ b/src/components/MobilityPlatform/AirportFlights/components/AirportFlightsContent/__tests__/__snapshots__/AirportFlightsContent.test.js.snap @@ -0,0 +1,59 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[` should match snapshot 1`] = ` +
    +
    +
    +

    + Turun lentoasema +

    +
    +
    +
    +

    + Lähtevät lennot: +

    +
    +
    +

    + Ei lähteviä lentoja. +

    +
    +
    +
    +
    +

    + Saapuvat lennot: +

    +
    +
    +

    + Ei saapuvia lentoja. +

    +
    +
    +
    +
    +`; diff --git a/src/components/MobilityPlatform/AirportFlights/components/AirportFlightsContent/index.js b/src/components/MobilityPlatform/AirportFlights/components/AirportFlightsContent/index.js new file mode 100644 index 000000000..30e77b698 --- /dev/null +++ b/src/components/MobilityPlatform/AirportFlights/components/AirportFlightsContent/index.js @@ -0,0 +1,3 @@ +import AirportFlightsContent from './AirportFlightsContent'; + +export default AirportFlightsContent; diff --git a/src/components/MobilityPlatform/AirportFlights/index.js b/src/components/MobilityPlatform/AirportFlights/index.js new file mode 100644 index 000000000..ac57a7d48 --- /dev/null +++ b/src/components/MobilityPlatform/AirportFlights/index.js @@ -0,0 +1,3 @@ +import AirportFlights from './AirportFlights'; + +export default AirportFlights; diff --git a/src/components/MobilityPlatform/BarbecuePlaces/components/BarbecuePlacesContent/BarbecuePlacesContent.js b/src/components/MobilityPlatform/BarbecuePlaces/components/BarbecuePlacesContent/BarbecuePlacesContent.js index e71f1d8d6..2e9e2533c 100644 --- a/src/components/MobilityPlatform/BarbecuePlaces/components/BarbecuePlacesContent/BarbecuePlacesContent.js +++ b/src/components/MobilityPlatform/BarbecuePlaces/components/BarbecuePlacesContent/BarbecuePlacesContent.js @@ -2,40 +2,40 @@ import PropTypes from 'prop-types'; import React from 'react'; import { Typography } from '@mui/material'; import { useIntl } from 'react-intl'; -import styled from '@emotion/styled'; +import { StyledContainer, StyledHeaderContainer, StyledTextContainer } from '../../../styled/styled'; const BarbecuePlacesContent = ({ item }) => { const intl = useIntl(); + return ( - - + + {intl.formatMessage({ id: 'mobilityPlatform.content.barbecuePlace.title' })} - - - - {`${item.extra.malli.trim()} (${item.extra.valmistaja})`} - - + +
    + + + {intl.formatMessage( + { id: 'mobilityPlatform.content.barbecuePlace.manufacturer' }, + { value: item.extra.valmistaja }, + )} + + + + + {intl.formatMessage( + { id: 'mobilityPlatform.content.barbecuePlace.model' }, + { value: item.extra.malli.trim() }, + )} + + +
    ); }; -const StyledContainer = styled.div(({ theme }) => ({ - margin: theme.spacing(1), -})); - -const StyledHeader = styled.div(({ theme }) => ({ - width: '85%', - borderBottom: '1px solid #000', - paddingBottom: theme.spacing(0.5), -})); - -const StyledText = styled.div(({ theme }) => ({ - marginTop: theme.spacing(0.5), -})); - BarbecuePlacesContent.propTypes = { item: PropTypes.shape({ extra: PropTypes.shape({ diff --git a/src/components/MobilityPlatform/BarbecuePlaces/components/BarbecuePlacesContent/__tests__/BarbecuePlacesContent.test.js b/src/components/MobilityPlatform/BarbecuePlaces/components/BarbecuePlacesContent/__tests__/BarbecuePlacesContent.test.js new file mode 100644 index 000000000..67e127d5f --- /dev/null +++ b/src/components/MobilityPlatform/BarbecuePlaces/components/BarbecuePlacesContent/__tests__/BarbecuePlacesContent.test.js @@ -0,0 +1,42 @@ +// Link.react.test.js +import React from 'react'; +import BarbecuePlacesContent from '../index'; +import { getRenderWithProviders } from '../../../../../../../jestUtils'; +import finnishTranslations from '../../../../../../i18n/fi'; + +const mockProps = { + item: { + extra: { + valmistaja: 'Testi', + malli: 'Testigrilli', + }, + }, +}; + +const renderWithProviders = getRenderWithProviders({}); + +describe('', () => { + it('should work', () => { + const { container } = renderWithProviders(); + expect(container).toMatchSnapshot(); + }); + + it('does show text correctly', () => { + const { container } = renderWithProviders(); + + const p = container.querySelectorAll('p'); + expect(p[0].textContent).toContain(`${finnishTranslations['mobilityPlatform.content.barbecuePlace.title']}`); + expect(p[1].textContent).toContain( + `${finnishTranslations['mobilityPlatform.content.barbecuePlace.manufacturer'].replace( + '{value}', + `${mockProps.item.extra.valmistaja}`, + )}`, + ); + expect(p[2].textContent).toContain( + `${finnishTranslations['mobilityPlatform.content.barbecuePlace.model'].replace( + '{value}', + `${mockProps.item.extra.malli}`, + )}`, + ); + }); +}); diff --git a/src/components/MobilityPlatform/BarbecuePlaces/components/BarbecuePlacesContent/__tests__/__snapshots__/BarbecuePlacesContent.test.js.snap b/src/components/MobilityPlatform/BarbecuePlaces/components/BarbecuePlacesContent/__tests__/__snapshots__/BarbecuePlacesContent.test.js.snap new file mode 100644 index 000000000..d8dd727eb --- /dev/null +++ b/src/components/MobilityPlatform/BarbecuePlaces/components/BarbecuePlacesContent/__tests__/__snapshots__/BarbecuePlacesContent.test.js.snap @@ -0,0 +1,39 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[` should work 1`] = ` +
    +
    +
    +

    + Grillaus- ja tulentekopaikka +

    +
    +
    +
    +

    + Valmistaja: Testi +

    +
    +
    +

    + Malli: Testigrilli +

    +
    +
    +
    +
    +`; diff --git a/src/components/MobilityPlatform/BicycleRoutes/BicycleRoutes.js b/src/components/MobilityPlatform/BicycleRoutes/BicycleRoutes.js index b44f242d1..b6b5ccd41 100644 --- a/src/components/MobilityPlatform/BicycleRoutes/BicycleRoutes.js +++ b/src/components/MobilityPlatform/BicycleRoutes/BicycleRoutes.js @@ -23,12 +23,15 @@ const BicycleRoutes = () => { const blackOptions = blackOptionsBase({ dashArray: '2 10 10 10' }); useEffect(() => { + const controller = new AbortController(); + const { signal } = controller; if (openMobilityPlatform) { - fetchBicycleRoutesGeometry(setBicycleRoutes); + fetchBicycleRoutesGeometry(setBicycleRoutes, signal); } + return () => controller.abort(); }, [openMobilityPlatform, setBicycleRoutes]); - const getActiveRoutes = (data) => { + const getActiveRoutes = data => { if (data && data.length > 0) { return data.filter(item => item.bicycle_network_name === bicycleRouteName); } @@ -45,24 +48,22 @@ const BicycleRoutes = () => { }, [showBicycleRoutes, activeBicycleRoute]); return ( - <> - {renderData - ? activeBicycleRoute.map(item => ( - - - - - )) - : null} - + renderData + ? activeBicycleRoute.map(item => ( + + + + + )) + : null ); }; diff --git a/src/components/MobilityPlatform/BicycleStands/components/BicycleStandContent/BicycleStandContent.js b/src/components/MobilityPlatform/BicycleStands/components/BicycleStandContent/BicycleStandContent.js index e4c9db772..7e6a81c88 100644 --- a/src/components/MobilityPlatform/BicycleStands/components/BicycleStandContent/BicycleStandContent.js +++ b/src/components/MobilityPlatform/BicycleStands/components/BicycleStandContent/BicycleStandContent.js @@ -1,10 +1,12 @@ import { Typography } from '@mui/material'; import PropTypes from 'prop-types'; import React from 'react'; +import { useIntl } from 'react-intl'; +import { StyledContainer, StyledHeaderContainer, StyledTextContainer } from '../../../styled/styled'; + +const BicycleStandContent = ({ bicycleStand }) => { + const intl = useIntl(); -const BicycleStandContent = ({ - classes, bicycleStand, intl, -}) => { const messageIds = { model: 'mobilityPlatform.content.bicycleStands.model', places: 'mobilityPlatform.content.bicycleStands.numOfPlaces', @@ -17,96 +19,104 @@ const BicycleStandContent = ({ }; const titleTypo = () => ( -
    - + + {bicycleStand.name} -
    + ); const multiValueTypo = messages => ( -
    +
    {bicycleStand.extra.model ? ( -
    + {intl.formatMessage({ id: messages.model, }, { value: bicycleStand.extra.model })} -
    + ) : null} -
    + {intl.formatMessage({ id: messages.places, }, { value: bicycleStand.extra.number_of_places })} -
    -
    + + {intl.formatMessage({ id: messages.stands, }, { value: bicycleStand.extra.number_of_stands })} -
    + {bicycleStand.extra.covered ? ( -
    + {intl.formatMessage({ id: messages.covered, })} -
    + ) : ( -
    + {intl.formatMessage({ id: messages.notCovered, })} -
    + )} {bicycleStand.extra.hull_lockable ? ( -
    + {intl.formatMessage({ id: messages.lockable, })} -
    + ) : ( -
    + {intl.formatMessage({ id: messages.notLockable, })} -
    + )} {bicycleStand.extra.maintained_by_turku ? ( -
    + {intl.formatMessage({ id: messages.maintained, })} -
    + ) : null}
    ); return ( -
    + {titleTypo()} {multiValueTypo(messageIds)} -
    + ); }; BicycleStandContent.propTypes = { - classes: PropTypes.objectOf(PropTypes.any).isRequired, - bicycleStand: PropTypes.objectOf(PropTypes.any), - intl: PropTypes.objectOf(PropTypes.any).isRequired, + bicycleStand: PropTypes.shape({ + name: PropTypes.string, + extra: PropTypes.shape({ + model: PropTypes.string, + number_of_places: PropTypes.number, + number_of_stands: PropTypes.number, + covered: PropTypes.bool, + hull_lockable: PropTypes.bool, + maintained_by_turku: PropTypes.bool, + }), + }), }; BicycleStandContent.defaultProps = { diff --git a/src/components/MobilityPlatform/BicycleStands/components/BicycleStandContent/__tests__/__snapshots__/BicycleStandContent.test.js.snap b/src/components/MobilityPlatform/BicycleStands/components/BicycleStandContent/__tests__/__snapshots__/BicycleStandContent.test.js.snap index 1935e4d12..b2b27f225 100644 --- a/src/components/MobilityPlatform/BicycleStands/components/BicycleStandContent/__tests__/__snapshots__/BicycleStandContent.test.js.snap +++ b/src/components/MobilityPlatform/BicycleStands/components/BicycleStandContent/__tests__/__snapshots__/BicycleStandContent.test.js.snap @@ -3,20 +3,20 @@ exports[` should work 1`] = `

    Testinimi

    should work 1`] = `

    should work 1`] = `

    should work 1`] = `

    should work 1`] = `

    should work 1`] = `

    ({ - title: { - marginBottom: theme.spacing(1), - width: '85%', - borderBottom: '1px solid #000000', - }, - titleText: { - fontSize: '0.9rem', - }, - paragraph: { - marginBottom: theme.spacing(0.5), - }, - padding: { - padding: theme.spacing(1), - }, -}); diff --git a/src/components/MobilityPlatform/BicycleStands/index.js b/src/components/MobilityPlatform/BicycleStands/index.js index 8c9a86d52..3235dfe69 100644 --- a/src/components/MobilityPlatform/BicycleStands/index.js +++ b/src/components/MobilityPlatform/BicycleStands/index.js @@ -1,6 +1,3 @@ -import { withStyles } from '@mui/styles'; -import { injectIntl } from 'react-intl'; import BicycleStands from './BicycleStands'; -import styles from './styles'; -export default withStyles(styles)(injectIntl(BicycleStands)); +export default BicycleStands; diff --git a/src/components/MobilityPlatform/BicycleStands/styles.js b/src/components/MobilityPlatform/BicycleStands/styles.js deleted file mode 100644 index bed7a8e7a..000000000 --- a/src/components/MobilityPlatform/BicycleStands/styles.js +++ /dev/null @@ -1,7 +0,0 @@ -const styles = { - popupInner: { - margin: '0.8rem', - }, -}; - -export default styles; diff --git a/src/components/MobilityPlatform/BikeServiceStations/components/BikeServiceStationContent/BikeServiceStationContent.js b/src/components/MobilityPlatform/BikeServiceStations/components/BikeServiceStationContent/BikeServiceStationContent.js index 7e65bc257..a6e1cb985 100644 --- a/src/components/MobilityPlatform/BikeServiceStations/components/BikeServiceStationContent/BikeServiceStationContent.js +++ b/src/components/MobilityPlatform/BikeServiceStations/components/BikeServiceStationContent/BikeServiceStationContent.js @@ -1,8 +1,9 @@ import PropTypes from 'prop-types'; import React from 'react'; +import { StyledContainer, StyledHeaderContainer, StyledTextContainer } from '../../../styled/styled'; import TextComponent from '../../../TextComponent'; -const BikeServiceStationContent = ({ classes, station }) => { +const BikeServiceStationContent = ({ station }) => { const stationName = { fi: station.name, en: station.name_en, @@ -22,27 +23,35 @@ const BikeServiceStationContent = ({ classes, station }) => { }; const bikeServiceStationInfo = ( -

    -
    + + -
    -
    + + {station.address ? : null} -
    -
    + + ); return ( - <> - {bikeServiceStationInfo} - + bikeServiceStationInfo ); }; BikeServiceStationContent.propTypes = { - classes: PropTypes.objectOf(PropTypes.any).isRequired, - station: PropTypes.objectOf(PropTypes.any), + station: PropTypes.shape({ + address: PropTypes.string, + address_fi: PropTypes.string, + address_en: PropTypes.string, + address_sv: PropTypes.string, + name: PropTypes.string, + name_en: PropTypes.string, + name_sv: PropTypes.string, + description: PropTypes.string, + description_en: PropTypes.string, + description_sv: PropTypes.string, + }), }; BikeServiceStationContent.defaultProps = { diff --git a/src/components/MobilityPlatform/BikeServiceStations/components/BikeServiceStationContent/__tests__/__snapshots__/BikeServiceStationContent.test.js.snap b/src/components/MobilityPlatform/BikeServiceStations/components/BikeServiceStationContent/__tests__/__snapshots__/BikeServiceStationContent.test.js.snap index a3a279cc9..df4075008 100644 --- a/src/components/MobilityPlatform/BikeServiceStations/components/BikeServiceStationContent/__tests__/__snapshots__/BikeServiceStationContent.test.js.snap +++ b/src/components/MobilityPlatform/BikeServiceStations/components/BikeServiceStationContent/__tests__/__snapshots__/BikeServiceStationContent.test.js.snap @@ -3,13 +3,13 @@ exports[` should match snapshot 1`] = `

    should match snapshot 1`] = `

    should match snapshot 1`] = `

    ({ - container: { - margin: theme.spacing(1), - }, - headerContainer: { - width: '85%', - borderBottom: '1px solid #000', - paddingBottom: theme.spacing(0.5), - }, - textContainer: { - marginTop: theme.spacing(0.5), - }, - contentInner: { - marginLeft: theme.spacing(1), - marginBottom: theme.spacing(1), - }, - margin: { - margin: theme.spacing(0.4), - }, -}); diff --git a/src/components/MobilityPlatform/Boating/Marinas/components/MarinasContent/MarinasContent.js b/src/components/MobilityPlatform/Boating/Marinas/components/MarinasContent/MarinasContent.js index 963a9a224..3d06a9420 100644 --- a/src/components/MobilityPlatform/Boating/Marinas/components/MarinasContent/MarinasContent.js +++ b/src/components/MobilityPlatform/Boating/Marinas/components/MarinasContent/MarinasContent.js @@ -1,14 +1,19 @@ import { Link, Typography } from '@mui/material'; import PropTypes from 'prop-types'; import React from 'react'; +import { useIntl } from 'react-intl'; +import styled from '@emotion/styled'; +import { StyledContainer, StyledHeaderContainer, StyledTextContainer } from '../../../../styled/styled'; + +const MarinasContent = ({ berthItem }) => { + const intl = useIntl(); -const MarinasContent = ({ - classes, intl, berthItem, -}) => { const renderText = (translationId, value) => ( - - {intl.formatMessage({ id: translationId }, { value })} - + + + {intl.formatMessage({ id: translationId }, { value })} + + ); const renderTypePrice = (price, berthType) => { @@ -16,20 +21,22 @@ const MarinasContent = ({ const fullPrice = price * alvTax; return ( <> - - - {intl.formatMessage({ id: 'mobilityPlatform.content.marinas.typeTitle' })} - - + + + + {intl.formatMessage({ id: 'mobilityPlatform.content.marinas.typeTitle' })} + + + {renderText('mobilityPlatform.content.marinas.type', berthType)} {renderText('mobilityPlatform.content.marinas.price', Math.round(fullPrice))} ); }; - const countBerths = (berthsData) => { + const countBerths = berthsData => { let count = 0; - berthsData.forEach((item) => { + berthsData.forEach(item => { if (item.Varaustyyppi === 'Venepaikat' || item.Varaustyyppi === 'Soutuvenepaikka') { count += 1; } @@ -39,9 +46,9 @@ const MarinasContent = ({ ); }; - const countWinterStorage = (berthsData) => { + const countWinterStorage = berthsData => { let count = 0; - berthsData.forEach((item) => { + berthsData.forEach(item => { if (item.Varaustyyppi === 'Soutuveneiden talvisäilytyspaikat' || item.Varaustyyppi === 'Talvisäilytyspaikat') { count += 1; } @@ -52,40 +59,56 @@ const MarinasContent = ({ }; const renderLink = (linkUrl, translationId) => ( - - - {intl.formatMessage({ - id: translationId, - })} - - + + + + {intl.formatMessage({ + id: translationId, + })} + + + ); return ( -

    -
    + + {berthItem.name} -
    -
    + +
    {countBerths(berthItem.extra.berths)} {berthItem.name === 'Satama: Lauttaranta' ? countWinterStorage(berthItem.extra.berths) : null} {renderTypePrice(berthItem.extra.berths[0].HintaAlv0, berthItem.extra.berths[0].Kohdetyyppi)} - - {intl.formatMessage({ id: 'mobilityPlatform.content.marinas.reservationInfo' })} - + + + {intl.formatMessage({ id: 'mobilityPlatform.content.marinas.reservationInfo' })} + + {renderLink('https://opaskartta.turku.fi/ePermit/fi/Reservation/', 'mobilityPlatform.info.marinas.link')} {renderLink('https://www.turku.fi/venepaikat', 'mobilityPlatform.content.marinas.infoLink')}
    -
    + ); }; +const StyledLinkText = styled(Typography)(({ theme }) => ({ + marginTop: theme.spacing(0.7), + color: theme.palette.link.main, + textDecoration: 'underline', +})); + MarinasContent.propTypes = { - classes: PropTypes.objectOf(PropTypes.any).isRequired, - intl: PropTypes.objectOf(PropTypes.any).isRequired, - berthItem: PropTypes.objectOf(PropTypes.any).isRequired, + berthItem: PropTypes.shape({ + name: PropTypes.string, + extra: PropTypes.shape({ + berths: PropTypes.arrayOf(PropTypes.shape({ + Kohdetyyppi: PropTypes.string, + HintaAlv0: PropTypes.number, + })), + }), + }).isRequired, }; export default MarinasContent; diff --git a/src/components/MobilityPlatform/Boating/Marinas/components/MarinasContent/__tests__/__snapshots__/MarinasContent.test.js.snap b/src/components/MobilityPlatform/Boating/Marinas/components/MarinasContent/__tests__/__snapshots__/MarinasContent.test.js.snap index 1ffd9ca3f..0f491f3b6 100644 --- a/src/components/MobilityPlatform/Boating/Marinas/components/MarinasContent/__tests__/__snapshots__/MarinasContent.test.js.snap +++ b/src/components/MobilityPlatform/Boating/Marinas/components/MarinasContent/__tests__/__snapshots__/MarinasContent.test.js.snap @@ -3,10 +3,10 @@ exports[` should work 1`] = `

    should work 1`] = ` Satama: Marina

    -
    -

    - Venepaikkojen määrä: 1 -

    -

    +

    - - Venetyyppi (esimerkki): - -

    -

    - Tyyppi: Aisapaikka -

    -

    + Venepaikkojen määrä: 1 +

    +
    +
    - Hinta: 124 € -

    -

    + + Venetyyppi (esimerkki): + +

    +
    +
    - Tiedot vapaista venepaikoista löytyvät varauspalvelusta. -

    - + Tyyppi: Aisapaikka +

    +
    +
    diff --git a/src/components/MobilityPlatform/Boating/Marinas/components/MarinasContent/index.js b/src/components/MobilityPlatform/Boating/Marinas/components/MarinasContent/index.js index 7603785d5..ef5d826b4 100644 --- a/src/components/MobilityPlatform/Boating/Marinas/components/MarinasContent/index.js +++ b/src/components/MobilityPlatform/Boating/Marinas/components/MarinasContent/index.js @@ -1,6 +1,3 @@ -import { withStyles } from '@mui/styles'; -import { injectIntl } from 'react-intl'; import MarinasContent from './MarinasContent'; -import styles from './styles'; -export default withStyles(styles)(injectIntl(MarinasContent)); +export default MarinasContent; diff --git a/src/components/MobilityPlatform/Boating/Marinas/components/MarinasContent/styles.js b/src/components/MobilityPlatform/Boating/Marinas/components/MarinasContent/styles.js deleted file mode 100644 index b7a71c8f1..000000000 --- a/src/components/MobilityPlatform/Boating/Marinas/components/MarinasContent/styles.js +++ /dev/null @@ -1,21 +0,0 @@ -export default theme => ({ - container: { - margin: theme.spacing(1), - }, - headerContainer: { - width: '85%', - borderBottom: '1px solid #000', - paddingBottom: theme.spacing(0.5), - }, - textContainer: { - marginTop: theme.spacing(0.5), - }, - margin: { - marginTop: theme.spacing(0.7), - }, - link: { - marginTop: theme.spacing(0.7), - color: theme.palette.link.main, - textDecoration: 'underline', - }, -}); diff --git a/src/components/MobilityPlatform/ChargerStationMarkers/components/ChargerStationContent/ChargerStationContent.js b/src/components/MobilityPlatform/ChargerStationMarkers/components/ChargerStationContent/ChargerStationContent.js index eeafc03f6..52f87dad5 100644 --- a/src/components/MobilityPlatform/ChargerStationMarkers/components/ChargerStationContent/ChargerStationContent.js +++ b/src/components/MobilityPlatform/ChargerStationMarkers/components/ChargerStationContent/ChargerStationContent.js @@ -1,26 +1,31 @@ import { Typography } from '@mui/material'; import PropTypes from 'prop-types'; import React from 'react'; +import styled from '@emotion/styled'; +import { useIntl } from 'react-intl'; +import { + StyledContainer, StyledHeaderContainer, StyledTextContainer, +} from '../../../styled/styled'; import TextComponent from '../../../TextComponent'; -const ChargerStationContent = ({ classes, intl, station }) => { - const titleTypo = (messageId, props = {}) => ( -
    +const ChargerStationContent = ({ station }) => { + const intl = useIntl(); + + const titleTypo = messageId => ( + {intl.formatMessage({ id: messageId, })} : -
    + ); - const singleValTypo = (messageId, value, props = {}) => ( -
    - - {intl.formatMessage({ id: messageId }, { value })} - -
    + const singleValTypo = (messageId, value) => ( + + {intl.formatMessage({ id: messageId }, { value })} + ); const stationName = { @@ -35,7 +40,7 @@ const ChargerStationContent = ({ classes, intl, station }) => { sv: station.address_sv, }; - const renderAdministrator = (item) => { + const renderAdministrator = item => { const stationAdmin = { fi: item.fi, en: item.en, @@ -45,10 +50,10 @@ const ChargerStationContent = ({ classes, intl, station }) => { return ; }; - const renderPayment = (paymentType, props = {}) => { + const renderPayment = paymentType => { const toLower = paymentType.toLowerCase(); return ( -
    + {intl.formatMessage({ id: @@ -57,7 +62,7 @@ const ChargerStationContent = ({ classes, intl, station }) => { : 'mobilityPlatform.chargerStations.content.free', })} -
    + ); }; @@ -66,38 +71,60 @@ const ChargerStationContent = ({ classes, intl, station }) => { <> {station.address ? : null} {station.extra.administrator.fi !== '' ? renderAdministrator(station.extra.administrator) : null} - {renderPayment(station.extra.payment, { className: classes.margin })} - {titleTypo('mobilityPlatform.content.chargersTitle', { className: classes.margin })} + {renderPayment(station.extra.payment)} + {titleTypo('mobilityPlatform.content.chargersTitle')} {station.extra.chargers && station.extra.chargers.length > 0 ? station.extra.chargers.map(charger => ( -
    + {singleValTypo('mobilityPlatform.content.cgsType', charger.plug)} {singleValTypo('mobilityPlatform.content.count', charger.number)} {intl.formatMessage({ id: 'mobilityPlatform.content.power' }, { value: charger.power })} -
    + )) : null} ); return ( -
    -
    + + -
    -
    - {chargerStationInfo} -
    -
    + +
    {chargerStationInfo}
    + ); }; +const StyledContentInner = styled.div(({ theme }) => ({ + marginLeft: theme.spacing(1), + marginBottom: theme.spacing(1), +})); + ChargerStationContent.propTypes = { - classes: PropTypes.objectOf(PropTypes.any).isRequired, - intl: PropTypes.objectOf(PropTypes.any).isRequired, - station: PropTypes.objectOf(PropTypes.any), + station: PropTypes.shape({ + address: PropTypes.string, + address_fi: PropTypes.string, + address_en: PropTypes.string, + address_sv: PropTypes.string, + name: PropTypes.string, + name_en: PropTypes.string, + name_sv: PropTypes.string, + extra: PropTypes.shape({ + payment: PropTypes.string, + administrator: PropTypes.shape({ + fi: PropTypes.string, + }), + chargers: PropTypes.arrayOf( + PropTypes.shape({ + plug: PropTypes.string, + number: PropTypes.string, + power: PropTypes.string, + }), + ), + }), + }), }; ChargerStationContent.defaultProps = { diff --git a/src/components/MobilityPlatform/ChargerStationMarkers/components/ChargerStationContent/__tests__/__snapshots__/ChargerStationContent.test.js.snap b/src/components/MobilityPlatform/ChargerStationMarkers/components/ChargerStationContent/__tests__/__snapshots__/ChargerStationContent.test.js.snap index a0375f274..1b3ce8c58 100644 --- a/src/components/MobilityPlatform/ChargerStationMarkers/components/ChargerStationContent/__tests__/__snapshots__/ChargerStationContent.test.js.snap +++ b/src/components/MobilityPlatform/ChargerStationMarkers/components/ChargerStationContent/__tests__/__snapshots__/ChargerStationContent.test.js.snap @@ -3,13 +3,13 @@ exports[` should match snapshot 1`] = `

    should match snapshot 1`] = `

    -
    +

    should match snapshot 1`] = `

    should match snapshot 1`] = `

    should match snapshot 1`] = `

    should match snapshot 1`] = `

    -
    +

    Sähkölatausaseman tyyppi: CCS

    -
    +

    diff --git a/src/components/MobilityPlatform/ChargerStationMarkers/components/ChargerStationContent/index.js b/src/components/MobilityPlatform/ChargerStationMarkers/components/ChargerStationContent/index.js index df2828ca2..9f82ac180 100644 --- a/src/components/MobilityPlatform/ChargerStationMarkers/components/ChargerStationContent/index.js +++ b/src/components/MobilityPlatform/ChargerStationMarkers/components/ChargerStationContent/index.js @@ -1,6 +1,3 @@ -import { withStyles } from '@mui/styles'; -import { injectIntl } from 'react-intl'; import ChargerStationContent from './ChargerStationContent'; -import styles from './styles'; -export default withStyles(styles)(injectIntl(ChargerStationContent)); +export default ChargerStationContent; diff --git a/src/components/MobilityPlatform/ChargerStationMarkers/components/ChargerStationContent/styles.js b/src/components/MobilityPlatform/ChargerStationMarkers/components/ChargerStationContent/styles.js deleted file mode 100644 index 6818cfb53..000000000 --- a/src/components/MobilityPlatform/ChargerStationMarkers/components/ChargerStationContent/styles.js +++ /dev/null @@ -1,20 +0,0 @@ -export default theme => ({ - container: { - margin: theme.spacing(1), - }, - headerContainer: { - width: '85%', - borderBottom: '1px solid #000', - paddingBottom: theme.spacing(0.5), - }, - textContainer: { - marginTop: theme.spacing(0.5), - }, - contentInner: { - marginLeft: theme.spacing(1), - marginBottom: theme.spacing(1), - }, - margin: { - margin: theme.spacing(0.4), - }, -}); diff --git a/src/components/MobilityPlatform/CityBikes/CityBikes.js b/src/components/MobilityPlatform/CityBikes/CityBikes.js index fd495cdfe..9e4161671 100644 --- a/src/components/MobilityPlatform/CityBikes/CityBikes.js +++ b/src/components/MobilityPlatform/CityBikes/CityBikes.js @@ -12,14 +12,13 @@ import cargoBikesIconProvider from 'servicemap-ui-turku/assets/icons/icons-icon_ import cargoBikesIconProviderBw from 'servicemap-ui-turku/assets/icons/contrast/icons-icon_cargo_bikes_provider-bw.svg'; import { useMobilityPlatformContext } from '../../../context/MobilityPlatformContext'; import { useAccessibleMap } from '../../../redux/selectors/settings'; -import { fetchCityBikesData } from '../mobilityPlatformRequests/mobilityPlatformRequests'; +import useIotDataFetch from '../utils/useIotDataFetch'; import { isDataValid, setRender, checkMapType } from '../utils/utils'; import { isEmbed } from '../../../utils/path'; +import { StyledPopupWrapper, StyledPopupInner } from '../styled/styled'; import CityBikesContent from './components/CityBikesContent'; const CityBikes = () => { - const [cityBikeStationsData, setCityBikeStationsData] = useState([]); - const [cityBikeStatistics, setCityBikeStatistics] = useState([]); const [zoomLevel, setZoomLevel] = useState(13); const { showCityBikes, showCargoBikes } = useMobilityPlatformContext(); @@ -54,22 +53,15 @@ const CityBikes = () => { iconSize: zoomLevel < 14 ? [45, 45] : [35, 35], }); - useEffect(() => { - if (showCityBikes || showCargoBikes || embedded) { - fetchCityBikesData('CBI', setCityBikeStationsData); - } - }, [showCityBikes, showCargoBikes, embedded]); + const fetchData = showCityBikes || showCargoBikes; - useEffect(() => { - if (showCityBikes || showCargoBikes || embedded) { - fetchCityBikesData('CBS', setCityBikeStatistics); - } - }, [showCityBikes, showCargoBikes, embedded]); + const { iotData: cityBikeStationsData } = useIotDataFetch('CBI', fetchData, embedded); + const { iotData: cityBikeStatistics } = useIotDataFetch('CBS', fetchData, embedded); const cityBikeStations = []; /** Separate cargo bike stations from city bike stations */ - const cargoBikeStations = cityBikeStationsData.reduce((acc, curr) => { + const cargoBikeStations = cityBikeStationsData?.reduce((acc, curr) => { if (curr.name.includes('eCargo bikes')) { acc.push(curr); } else { @@ -86,7 +78,7 @@ const CityBikes = () => { const fitBounds = (renderData, data) => { if (renderData) { const bounds = []; - data.forEach((item) => { + data.forEach(item => { bounds.push([item.lat, item.lon]); }); map.fitBounds(bounds); @@ -101,15 +93,19 @@ const CityBikes = () => { }, [showCityBikes, showCargoBikes]); const renderCityBikeMarkers = (isValid, data, icon) => (isValid ? ( - data.map((item) => ( + data.map(item => ( - - - + + + + + + + )) ) : null); diff --git a/src/components/MobilityPlatform/CityBikes/components/CityBikesContent/CityBikesContent.js b/src/components/MobilityPlatform/CityBikes/components/CityBikesContent/CityBikesContent.js index 3e238fc14..6b7c91fee 100644 --- a/src/components/MobilityPlatform/CityBikes/components/CityBikesContent/CityBikesContent.js +++ b/src/components/MobilityPlatform/CityBikes/components/CityBikesContent/CityBikesContent.js @@ -1,10 +1,14 @@ import { Link, Typography } from '@mui/material'; import PropTypes from 'prop-types'; import React from 'react'; +import { useIntl } from 'react-intl'; +import styled from '@emotion/styled'; +import { + StyledContainer, StyledHeaderContainer, StyledTextContainer, StyledBoldText, +} from '../../../styled/styled'; -const CityBikesContent = ({ - classes, intl, bikeStation, cityBikeStatistics, -}) => { +const CityBikesContent = ({ bikeStation, cityBikeStatistics }) => { + const intl = useIntl(); const getStationType = () => bikeStation.name.includes('eCargo bikes'); const isCargoBike = getStationType(); @@ -19,9 +23,9 @@ const CityBikesContent = ({ const stationItem = getStation(cityBikeStatistics); const renderText = (translationId, value) => ( -

    + {intl.formatMessage({ id: translationId }, { value })} -
    + ); /** Remove 'eCargo bikes' from station name before render */ @@ -31,21 +35,21 @@ const CityBikesContent = ({ }; const renderLink = (linkUrl, text) => ( -
    + - + {text} - + -
    + ); const renderStationType = (isVirtual, translationId) => { if (isVirtual) { return ( -
    + {intl.formatMessage({ id: translationId })} -
    + ); } return null; @@ -54,14 +58,14 @@ const CityBikesContent = ({ const getNumberOfVacantPlaces = (capacity, numberOfBikes) => capacity - numberOfBikes; return ( -
    -
    + + {intl.formatMessage({ id: isCargoBike ? 'mobilityPlatform.content.cargoBikes.title' : 'mobilityPlatform.content.cityBikes.title', })} -
    + {isCargoBike ? renderText('mobilityPlatform.content.cityBikes.name', formatStationName(bikeStation.name)) : renderText('mobilityPlatform.content.cityBikes.name', bikeStation.name)} @@ -86,23 +90,24 @@ const CityBikesContent = ({ )) : null}
    -
    - + + {intl.formatMessage({ id: 'mobilityPlatform.content.general.rentalUris' })} : - -
    + + {renderLink(bikeStation.rental_uris.android, 'Android')} {renderLink(bikeStation.rental_uris.ios, 'iOS')} -
    + ); }; +const StyledLinkText = styled(Typography)(({ theme }) => ({ + color: theme.palette.link.main, + textDecoration: 'underline', +})); + CityBikesContent.propTypes = { - intl: PropTypes.shape({ - formatMessage: PropTypes.func, - }).isRequired, - classes: PropTypes.objectOf(PropTypes.string).isRequired, bikeStation: PropTypes.shape({ station_id: PropTypes.string, name: PropTypes.string, diff --git a/src/components/MobilityPlatform/CityBikes/components/CityBikesContent/__tests__/__snapshots__/CityBikesContent.test.js.snap b/src/components/MobilityPlatform/CityBikes/components/CityBikesContent/__tests__/__snapshots__/CityBikesContent.test.js.snap index 3dd153c4c..f6fb4e386 100644 --- a/src/components/MobilityPlatform/CityBikes/components/CityBikesContent/__tests__/__snapshots__/CityBikesContent.test.js.snap +++ b/src/components/MobilityPlatform/CityBikes/components/CityBikesContent/__tests__/__snapshots__/CityBikesContent.test.js.snap @@ -3,10 +3,10 @@ exports[` should work 1`] = `

    should work 1`] = `

    should work 1`] = `

    should work 1`] = `

    should work 1`] = `

    should work 1`] = `

    Varauslinkit :

    should work 1`] = ` target="_blank" > diff --git a/src/components/MobilityPlatform/CityBikes/components/CityBikesContent/index.js b/src/components/MobilityPlatform/CityBikes/components/CityBikesContent/index.js index 02e8bbfb7..7b70b4508 100644 --- a/src/components/MobilityPlatform/CityBikes/components/CityBikesContent/index.js +++ b/src/components/MobilityPlatform/CityBikes/components/CityBikesContent/index.js @@ -1,6 +1,3 @@ -import { withStyles } from '@mui/styles'; -import { injectIntl } from 'react-intl'; import CityBikesContent from './CityBikesContent'; -import styles from './styles'; -export default withStyles(styles)(injectIntl(CityBikesContent)); +export default CityBikesContent; diff --git a/src/components/MobilityPlatform/CityBikes/components/CityBikesContent/styles.js b/src/components/MobilityPlatform/CityBikes/components/CityBikesContent/styles.js deleted file mode 100644 index ce800f972..000000000 --- a/src/components/MobilityPlatform/CityBikes/components/CityBikesContent/styles.js +++ /dev/null @@ -1,21 +0,0 @@ -export default theme => ({ - popupInner: { - margin: theme.spacing(2), - }, - subtitle: { - marginBottom: theme.spacing(1), - paddingBottom: theme.spacing(0.5), - borderBottom: '1px solid #000000', - width: '88%', - }, - paragraph: { - marginBottom: theme.spacing(0.4), - }, - bold: { - fontWeight: 'bold', - }, - link: { - color: theme.palette.link.main, - textDecoration: 'underline', - }, -}); diff --git a/src/components/MobilityPlatform/CrossWalks/CrossWalks.js b/src/components/MobilityPlatform/CrossWalks/CrossWalks.js index 30ce3d823..b86796364 100644 --- a/src/components/MobilityPlatform/CrossWalks/CrossWalks.js +++ b/src/components/MobilityPlatform/CrossWalks/CrossWalks.js @@ -1,5 +1,5 @@ /* eslint-disable global-require */ -import React, { useState } from 'react'; +import React, { useState, useEffect } from 'react'; import PropTypes from 'prop-types'; import { useSelector } from 'react-redux'; import { useMap, useMapEvents } from 'react-leaflet'; @@ -59,8 +59,10 @@ const CrossWalks = ({ mapObject }) => { const fetchBox = MapUtility.getBboxFromBounds(wideBounds, true); const isDetailZoom = zoomLevel >= mapObject.options.detailZoom; + const controller = new AbortController(); const handleCrossWalks = () => { + const { signal } = controller; const options = { type_name: 'CrossWalkSign', page_size: 3000, @@ -70,10 +72,12 @@ const CrossWalks = ({ mapObject }) => { (openMobilityPlatform && isDetailZoom) || (embedded && isDetailZoom) ) { - fetchMobilityMapData(options, setCrossWalksData); + fetchMobilityMapData(options, setCrossWalksData, signal); } }; + useEffect(() => () => controller.abort(), []); + const mapEvent = useMapEvents({ zoomend() { setZoomLevel(mapEvent.getZoom()); @@ -87,18 +91,16 @@ const CrossWalks = ({ mapObject }) => { const renderData = isDetailZoom && setRender(paramValue, embedded, showCrossWalks, crossWalksData, isDataValid); return ( - <> - {renderData - ? crossWalksData.map(item => ( - - - - )) - : null} - + renderData + ? crossWalksData.map(item => ( + + + + )) + : null ); }; diff --git a/src/components/MobilityPlatform/CultureRoutes/components/CultureRouteUnits/CultureRouteUnits.js b/src/components/MobilityPlatform/CultureRoutes/components/CultureRouteUnits/CultureRouteUnits.js index 73031c6e2..92ea6375e 100644 --- a/src/components/MobilityPlatform/CultureRoutes/components/CultureRouteUnits/CultureRouteUnits.js +++ b/src/components/MobilityPlatform/CultureRoutes/components/CultureRouteUnits/CultureRouteUnits.js @@ -5,6 +5,7 @@ import React from 'react'; import { useIntl } from 'react-intl'; import { useMap } from 'react-leaflet'; import { useSelector } from 'react-redux'; +import styled from '@emotion/styled'; import routeUnitIcon from 'servicemap-ui-turku/assets/icons/icons-icon_culture_route.svg'; import routeUnitIconBw from 'servicemap-ui-turku/assets/icons/contrast/icons-icon_culture_route-bw.svg'; import { useMobilityPlatformContext } from '../../../../../context/MobilityPlatformContext'; @@ -12,7 +13,7 @@ import { useAccessibleMap } from '../../../../../redux/selectors/settings'; import { createIcon } from '../../../utils/utils'; import useLocaleText from '../../../../../utils/useLocaleText'; -const CultureRouteUnits = ({ classes, cultureRouteUnits }) => { +const CultureRouteUnits = ({ cultureRouteUnits }) => { const { cultureRouteId } = useMobilityPlatformContext(); const intl = useIntl(); @@ -34,7 +35,7 @@ const CultureRouteUnits = ({ classes, cultureRouteUnits }) => { } }; - const filterRouteUnits = (data) => { + const filterRouteUnits = data => { if (data && data.length > 0) { return data.filter(item => item.mobile_unit_group.id === cultureRouteId); } @@ -91,41 +92,76 @@ const CultureRouteUnits = ({ classes, cultureRouteUnits }) => { * It is easier to make a custom close button than to edit the default close button */ - return ( - <> - {activeCultureRouteUnits + return (activeCultureRouteUnits && activeCultureRouteUnits.length > 0 ? ( - activeCultureRouteUnits.map(item => ( - - -
    -
    - - {getRouteUnitName(item.name, item.name_en, item.name_sv)} - - closePopup()} className={classes.popupCloseButton}> - - -
    -
    - {renderDescription( - item.description, - item.description_en, - item.description_sv, - )} -
    -
    -
    -
    - )) - ) : null} - + activeCultureRouteUnits.map(item => ( + + + + + + {getRouteUnitName(item.name, item.name_en, item.name_sv)} + + closePopup()}> + + + +
    + {renderDescription( + item.description, + item.description_en, + item.description_sv, + )} +
    +
    +
    +
    + )) + ) : null ); }; +const StyledContent = styled.div(({ theme }) => ({ + padding: theme.spacing(1.5), +})); + +const StyledHeader = styled.div(({ theme }) => ({ + width: '98%', + marginBottom: theme.spacing(1), + borderBottom: '1px solid rgba(0, 0, 0, 255)', + display: 'flex', + flexDirection: 'row', + justifyContent: 'space-between', + alignItems: 'center', +})); + +const StyledButton = styled(ButtonBase)(({ theme }) => ({ + padding: theme.spacing(1), + '&:hover': { + cursor: 'pointer', + borderRadius: '5px', + border: '1px solid rgba(0, 0, 0, 255)', + }, +})); + +const StyledCloseIcon = styled(Close)(() => ({ + fontSize: '1.25rem', + width: '1.25rem', + height: '1.25rem', + lineHeight: '1.4rem', +})); + CultureRouteUnits.propTypes = { - classes: PropTypes.objectOf(PropTypes.any).isRequired, - cultureRouteUnits: PropTypes.arrayOf(PropTypes.any), + cultureRouteUnits: PropTypes.arrayOf(PropTypes.shape({ + id: PropTypes.string, + name_fi: PropTypes.string, + name_en: PropTypes.string, + name_sv: PropTypes.string, + description: PropTypes.string, + description_en: PropTypes.string, + description_sv: PropTypes.string, + geometry_coords: PropTypes.objectOf(PropTypes.number), + })), }; CultureRouteUnits.defaultProps = { diff --git a/src/components/MobilityPlatform/CultureRoutes/components/CultureRouteUnits/index.js b/src/components/MobilityPlatform/CultureRoutes/components/CultureRouteUnits/index.js index ee8fd8413..f7a18e99a 100644 --- a/src/components/MobilityPlatform/CultureRoutes/components/CultureRouteUnits/index.js +++ b/src/components/MobilityPlatform/CultureRoutes/components/CultureRouteUnits/index.js @@ -1,6 +1,3 @@ -import { withStyles } from '@mui/styles'; -import { injectIntl } from 'react-intl'; import CultureRouteUnits from './CultureRouteUnits'; -import styles from './styles'; -export default injectIntl(withStyles(styles)(CultureRouteUnits)); +export default CultureRouteUnits; diff --git a/src/components/MobilityPlatform/CultureRoutes/components/CultureRouteUnits/styles.js b/src/components/MobilityPlatform/CultureRoutes/components/CultureRouteUnits/styles.js deleted file mode 100644 index 0614ca5df..000000000 --- a/src/components/MobilityPlatform/CultureRoutes/components/CultureRouteUnits/styles.js +++ /dev/null @@ -1,28 +0,0 @@ -export default theme => ({ - popupInner: { - padding: theme.spacing(1.5), - }, - header: { - width: '98%', - marginBottom: theme.spacing(1), - borderBottom: '1px solid rgba(0, 0, 0, 255)', - display: 'flex', - flexDirection: 'row', - justifyContent: 'space-between', - alignItems: 'center', - }, - popupCloseButton: { - padding: theme.spacing(1), - '&:hover': { - cursor: 'pointer', - borderRadius: '5px', - border: '1px solid rgba(0, 0, 0, 255)', - }, - }, - closeIcon: { - fontSize: '1.25rem', - width: '1.25rem', - height: '1.25rem', - lineHeight: '1.4rem', - }, -}); diff --git a/src/components/MobilityPlatform/GasFillingStationMarkers/components/GasFillingStationContent/GasFillingStationContent.js b/src/components/MobilityPlatform/GasFillingStationMarkers/components/GasFillingStationContent/GasFillingStationContent.js index 66cc420ef..c2dd3f5a2 100644 --- a/src/components/MobilityPlatform/GasFillingStationMarkers/components/GasFillingStationContent/GasFillingStationContent.js +++ b/src/components/MobilityPlatform/GasFillingStationMarkers/components/GasFillingStationContent/GasFillingStationContent.js @@ -1,42 +1,53 @@ import { Typography } from '@mui/material'; import PropTypes from 'prop-types'; import React from 'react'; +import { useIntl } from 'react-intl'; +import { + StyledContainer, StyledHeaderContainer, StyledTextContainer, +} from '../../../styled/styled'; -const GasFillingStationContent = ({ classes, intl, station }) => { - const singleValTypo = (messageId, value, props = {}) => ( -
    +const GasFillingStationContent = ({ station }) => { + const intl = useIntl(); + + const singleValTypo = (messageId, value) => ( + {intl.formatMessage({ id: messageId }, { value })} -
    + ); const gasFillingStationInfo = ( <> - {singleValTypo('mobilityPlatform.content.address', station.address, { className: classes.margin })} - {singleValTypo('mobilityPlatform.content.gfsType', station.extra.lng_cng, { className: classes.margin })} - {singleValTypo('mobilityPlatform.content.operator', station.extra.operator, { className: classes.margin })} + {singleValTypo('mobilityPlatform.content.address', station.address)} + {singleValTypo('mobilityPlatform.content.gfsType', station.extra.lng_cng)} + {singleValTypo('mobilityPlatform.content.operator', station.extra.operator)} ); return ( -
    -
    + + {station.name} -
    -
    + +
    {gasFillingStationInfo}
    -
    + ); }; GasFillingStationContent.propTypes = { - classes: PropTypes.objectOf(PropTypes.any).isRequired, - intl: PropTypes.objectOf(PropTypes.any).isRequired, - station: PropTypes.objectOf(PropTypes.any), + station: PropTypes.shape({ + name: PropTypes.string, + address: PropTypes.string, + extra: PropTypes.shape({ + operator: PropTypes.string, + lng_cng: PropTypes.string, + }), + }), }; GasFillingStationContent.defaultProps = { diff --git a/src/components/MobilityPlatform/GasFillingStationMarkers/components/GasFillingStationContent/__tests__/__snapshots__/GasFillingStationContent.test.js.snap b/src/components/MobilityPlatform/GasFillingStationMarkers/components/GasFillingStationContent/__tests__/__snapshots__/GasFillingStationContent.test.js.snap index d47f24580..c4267d6bb 100644 --- a/src/components/MobilityPlatform/GasFillingStationMarkers/components/GasFillingStationContent/__tests__/__snapshots__/GasFillingStationContent.test.js.snap +++ b/src/components/MobilityPlatform/GasFillingStationMarkers/components/GasFillingStationContent/__tests__/__snapshots__/GasFillingStationContent.test.js.snap @@ -3,10 +3,10 @@ exports[` should match snapshot 1`] = `

    should match snapshot 1`] = ` Testinimi

    -
    +

    should match snapshot 1`] = `

    should match snapshot 1`] = `

    ({ - container: { - margin: theme.spacing(1), - }, - headerContainer: { - width: '85%', - borderBottom: '1px solid #000', - paddingBottom: theme.spacing(0.5), - }, - textContainer: { - marginTop: theme.spacing(0.5), - }, - contentInner: { - marginLeft: theme.spacing(1), - marginBottom: theme.spacing(1), - }, - margin: { - margin: theme.spacing(0.4), - }, -}); diff --git a/src/components/MobilityPlatform/InfoTextBox/InfoTextBox.js b/src/components/MobilityPlatform/InfoTextBox/InfoTextBox.js index 9dea299ce..4665924e9 100644 --- a/src/components/MobilityPlatform/InfoTextBox/InfoTextBox.js +++ b/src/components/MobilityPlatform/InfoTextBox/InfoTextBox.js @@ -1,40 +1,52 @@ import { Link, Typography } from '@mui/material'; import PropTypes from 'prop-types'; import React from 'react'; +import { useIntl } from 'react-intl'; +import styled from '@emotion/styled'; +import { StyledLinkText } from '../styled/styled'; const InfoTextBox = ({ - classes, intl, infoText, linkUrl, linkText, reducePadding, -}) => ( -

    - - {intl.formatMessage({ - id: infoText, - })} - - {linkUrl ? ( - - - {intl.formatMessage({ - id: linkText, - })} - - - ) : null} -
    -); + infoText, linkUrl, linkText, reducePadding, removeBorder, +}) => { + const intl = useIntl(); + + return ( + + + {intl.formatMessage({ + id: infoText, + })} + + {linkUrl ? ( + + + {intl.formatMessage({ + id: linkText, + })} + + + ) : null} + + ); +}; + +const StyledContainer = styled.div(({ reducePadding, removeBorder }) => ({ + textAlign: 'left', + borderTop: removeBorder ? 'none' : '1px solid rgb(193, 193, 193)', + padding: reducePadding ? '0.5rem 0.5rem 0.5rem 0' : '1rem', +})); InfoTextBox.propTypes = { - classes: PropTypes.objectOf(PropTypes.any).isRequired, - intl: PropTypes.objectOf(PropTypes.any).isRequired, infoText: PropTypes.string, linkUrl: PropTypes.string, linkText: PropTypes.string, reducePadding: PropTypes.bool, + removeBorder: PropTypes.bool, }; InfoTextBox.defaultProps = { @@ -42,6 +54,7 @@ InfoTextBox.defaultProps = { linkUrl: '', linkText: '', reducePadding: false, + removeBorder: false, }; export default InfoTextBox; diff --git a/src/components/MobilityPlatform/InfoTextBox/__tests__/__snapshots__/InfoTextBox.test.js.snap b/src/components/MobilityPlatform/InfoTextBox/__tests__/__snapshots__/InfoTextBox.test.js.snap index d29f9467f..82ecb8774 100644 --- a/src/components/MobilityPlatform/InfoTextBox/__tests__/__snapshots__/InfoTextBox.test.js.snap +++ b/src/components/MobilityPlatform/InfoTextBox/__tests__/__snapshots__/InfoTextBox.test.js.snap @@ -3,7 +3,7 @@ exports[` should work 1`] = `

    ({ - container: { - textAlign: 'left', - borderTop: 'rgb(193, 193, 193)', - }, - link: { - marginTop: theme.spacing(0.5), - color: theme.palette.link.main, - textDecoration: 'underline', - }, - padding: { - padding: theme.spacing(2), - }, - paddingSm: { - padding: '1rem 0.5rem 0.5rem 0', - }, -}); diff --git a/src/components/MobilityPlatform/LoadingPlaces/components/LoadingPlacesContent/LoadingPlacesContent.js b/src/components/MobilityPlatform/LoadingPlaces/components/LoadingPlacesContent/LoadingPlacesContent.js index c6c73ed21..cee4e16d0 100644 --- a/src/components/MobilityPlatform/LoadingPlaces/components/LoadingPlacesContent/LoadingPlacesContent.js +++ b/src/components/MobilityPlatform/LoadingPlaces/components/LoadingPlacesContent/LoadingPlacesContent.js @@ -1,8 +1,10 @@ import PropTypes from 'prop-types'; import React from 'react'; +import styled from '@emotion/styled'; import TextComponent from '../../../TextComponent'; +import { StyledContainer, StyledHeaderContainer } from '../../../styled/styled'; -const LoadingPlacesContent = ({ classes, item }) => { +const LoadingPlacesContent = ({ item }) => { const loadingPlaceName = { fi: item.name_fi, en: item.name_en, @@ -16,29 +18,42 @@ const LoadingPlacesContent = ({ classes, item }) => { }; const loadingPlaceInfo = ( -

    -
    + + -
    -
    - {item.address_fi !== '' ? : null} + + + {item.address_fi !== '' ? ( + + ) : null} -
    -
    + + ); - return ( -
    - {loadingPlaceInfo} -
    - ); + return loadingPlaceInfo; }; +const StyledMargin = styled.div(({ theme }) => ({ + marginTop: theme.spacing(0.5), +})); + LoadingPlacesContent.propTypes = { - classes: PropTypes.objectOf(PropTypes.any).isRequired, - item: PropTypes.objectOf(PropTypes.any), + item: PropTypes.shape({ + address_fi: PropTypes.string, + address_en: PropTypes.string, + address_sv: PropTypes.string, + name_fi: PropTypes.string, + name_en: PropTypes.string, + name_sv: PropTypes.string, + extra: PropTypes.shape({ + lastauspiste: PropTypes.objectOf(PropTypes.string), + Saavutettavuus: PropTypes.objectOf(PropTypes.string), + rajoitustyyppi: PropTypes.objectOf(PropTypes.string), + }), + }), }; LoadingPlacesContent.defaultProps = { diff --git a/src/components/MobilityPlatform/LoadingPlaces/components/LoadingPlacesContent/__tests__/__snapshots__/LoadingPlacesContent.test.js.snap b/src/components/MobilityPlatform/LoadingPlaces/components/LoadingPlacesContent/__tests__/__snapshots__/LoadingPlacesContent.test.js.snap index 6255fb781..9331cc177 100644 --- a/src/components/MobilityPlatform/LoadingPlaces/components/LoadingPlacesContent/__tests__/__snapshots__/LoadingPlacesContent.test.js.snap +++ b/src/components/MobilityPlatform/LoadingPlaces/components/LoadingPlacesContent/__tests__/__snapshots__/LoadingPlacesContent.test.js.snap @@ -3,63 +3,59 @@ exports[` should match snapshot 1`] = `
    -
    -

    - Testi -

    -
    + Testi +

    +
    +
    -
    -

    - Osoite: Testikatu -

    -
    -
    +
    +
    +

    -

    - Testi -

    -
    -
    +
    +
    +

    -

    - Testitieto -

    -
    -
    +
    +
    +

    -

    - Testitieto -

    -
    + Testitieto +

    diff --git a/src/components/MobilityPlatform/LoadingPlaces/components/LoadingPlacesContent/index.js b/src/components/MobilityPlatform/LoadingPlaces/components/LoadingPlacesContent/index.js index 912db4a7b..e18b49684 100644 --- a/src/components/MobilityPlatform/LoadingPlaces/components/LoadingPlacesContent/index.js +++ b/src/components/MobilityPlatform/LoadingPlaces/components/LoadingPlacesContent/index.js @@ -1,5 +1,3 @@ -import { withStyles } from '@mui/styles'; import LoadingPlacesContent from './LoadingPlacesContent'; -import styles from './styles'; -export default withStyles(styles)(LoadingPlacesContent); +export default LoadingPlacesContent; diff --git a/src/components/MobilityPlatform/LoadingPlaces/components/LoadingPlacesContent/styles.js b/src/components/MobilityPlatform/LoadingPlaces/components/LoadingPlacesContent/styles.js deleted file mode 100644 index a3d8089ef..000000000 --- a/src/components/MobilityPlatform/LoadingPlaces/components/LoadingPlacesContent/styles.js +++ /dev/null @@ -1,23 +0,0 @@ -export default theme => ({ - container: { - margin: theme.spacing(1), - }, - headerContainer: { - width: '85%', - borderBottom: '1px solid #000', - paddingBottom: theme.spacing(0.5), - }, - textContainer: { - marginTop: theme.spacing(0.5), - }, - contentInner: { - marginLeft: theme.spacing(1), - marginBottom: theme.spacing(1), - }, - margin: { - margin: theme.spacing(0.4), - }, - marginTop: { - marginTop: theme.spacing(0.4), - }, -}); diff --git a/src/components/MobilityPlatform/MarkerComponent/MarkerComponent.js b/src/components/MobilityPlatform/MarkerComponent/MarkerComponent.js index 0ea38cf7d..4b09be32a 100644 --- a/src/components/MobilityPlatform/MarkerComponent/MarkerComponent.js +++ b/src/components/MobilityPlatform/MarkerComponent/MarkerComponent.js @@ -1,26 +1,29 @@ import React from 'react'; import { PropTypes } from 'prop-types'; +import { StyledPopupWrapper, StyledPopupInner } from '../styled/styled'; -const MarkerComponent = ({ - classes, item, icon, children, -}) => { +const MarkerComponent = ({ item, icon, children }) => { const { Marker, Popup } = global.rL; return ( -
    + -
    {children}
    + {children}
    -
    +
    ); }; MarkerComponent.propTypes = { - classes: PropTypes.objectOf(PropTypes.any).isRequired, - item: PropTypes.objectOf(PropTypes.any), - icon: PropTypes.objectOf(PropTypes.any).isRequired, + item: PropTypes.shape({ + geometry_coords: PropTypes.objectOf(PropTypes.number), + }), + icon: PropTypes.shape({ + path: PropTypes.string, + viewBox: PropTypes.string, + }).isRequired, children: PropTypes.node, }; diff --git a/src/components/MobilityPlatform/MarkerComponent/index.js b/src/components/MobilityPlatform/MarkerComponent/index.js index 7582480e2..a5c880fab 100644 --- a/src/components/MobilityPlatform/MarkerComponent/index.js +++ b/src/components/MobilityPlatform/MarkerComponent/index.js @@ -1,5 +1,3 @@ -import { withStyles } from '@mui/styles'; import MarkerComponent from './MarkerComponent'; -import styles from './styles'; -export default withStyles(styles)(MarkerComponent); +export default MarkerComponent; diff --git a/src/components/MobilityPlatform/MarkerComponent/styles.js b/src/components/MobilityPlatform/MarkerComponent/styles.js deleted file mode 100644 index de9de2381..000000000 --- a/src/components/MobilityPlatform/MarkerComponent/styles.js +++ /dev/null @@ -1,17 +0,0 @@ -const styles = theme => ({ - popupWrapper: { - position: 'absolute', - textAlign: 'center', - marginBottom: theme.spacing(2), - width: '429px', - }, - popupInner: { - borderRadius: '3px', - marginBottom: theme.spacing(0.5), - marginLeft: theme.spacing(0.8), - lineHeight: 1.2, - overflowX: 'hidden', - }, -}); - -export default styles; diff --git a/src/components/MobilityPlatform/MobilityProfiles/MobilityProfiles.js b/src/components/MobilityPlatform/MobilityProfiles/MobilityProfiles.js new file mode 100644 index 000000000..dc314bf83 --- /dev/null +++ b/src/components/MobilityPlatform/MobilityProfiles/MobilityProfiles.js @@ -0,0 +1,198 @@ +import React, { useEffect, useState } from 'react'; +import { useSelector } from 'react-redux'; +import { useMap } from 'react-leaflet'; +import moose from 'servicemap-ui-turku/assets/icons/icons-icon_moose.svg'; +import fox from 'servicemap-ui-turku/assets/icons/icons-icon_fox.svg'; +import deer from 'servicemap-ui-turku/assets/icons/icons-icon_deer.svg'; +import rabbit from 'servicemap-ui-turku/assets/icons/icons-icon_rabbit.svg'; +import marten from 'servicemap-ui-turku/assets/icons/icons-icon_marten.svg'; +import capercaillie from 'servicemap-ui-turku/assets/icons/icons-icon_capercaillie.svg'; +import mooseContrast from 'servicemap-ui-turku/assets/icons/contrast/icons-icon_moose-bw.svg'; +import foxContrast from 'servicemap-ui-turku/assets/icons/contrast/icons-icon_fox-bw.svg'; +import deerContrast from 'servicemap-ui-turku/assets/icons/contrast/icons-icon_deer-bw.svg'; +import rabbitContrast from 'servicemap-ui-turku/assets/icons/contrast/icons-icon_rabbit-bw.svg'; +import martenContrast from 'servicemap-ui-turku/assets/icons/contrast/icons-icon_marten-bw.svg'; +import capercaillieContrast from 'servicemap-ui-turku/assets/icons/contrast/icons-icon_capercaillie-bw.svg'; +import { useMobilityPlatformContext } from '../../../context/MobilityPlatformContext'; +import { useAccessibleMap } from '../../../redux/selectors/settings'; +import { + isDataValid, createIcon, blueOptionsBase, whiteOptionsBase, +} from '../utils/utils'; +import { fetchPostCodeAreas, fetchMobilityProfilesData } from '../mobilityPlatformRequests/mobilityPlatformRequests'; +import { StyledPopupWrapper, StyledPopupInner } from '../styled/styled'; +import MobilityProfilesContent from './components/MobilityProfilesContent'; + +const MobilityProfiles = () => { + const [postCodeAreas, setPostCodeAreas] = useState([]); + const [mobilityProfilesData, setMobilityProfilesData] = useState([]); + + const { showMobilityResults } = useMobilityPlatformContext(); + + const { Marker, Polygon, Popup } = global.rL; + const { icon, polygon } = global.L; + + const map = useMap(); + const useContrast = useSelector(useAccessibleMap); + + useEffect(() => { + const controller = new AbortController(); + const { signal } = controller; + if (showMobilityResults) { + fetchPostCodeAreas(setPostCodeAreas, signal); + fetchMobilityProfilesData(setMobilityProfilesData, signal); + } + return () => controller.abort(); + }, [showMobilityResults]); + + /** + * Filter postal codes based on what areas include results data. Also filter areas with 0 result count. + * @param {array} data1 + * @param {array} data2 + * @returns array of objects + */ + const filterPostCodes = (data1, data2) => data1.filter(item => data2.some( + el => el.postal_code_string === item.name.fi && el.postal_code_type_string === 'Home' && el.count >= 1, + )); + const filteredPostCodes = filterPostCodes(postCodeAreas, mobilityProfilesData); + + /** + * Swap coordinates to be in correct Leaflet format. + * @param {array} inputData + * @returns array + */ + const swapCoords = inputData => { + if (inputData?.length) { + return inputData.map(item => item.map(v => v.map(j => [j[1], j[0]]))); + } + return inputData; + }; + + const blueColor = blueOptionsBase({ + weight: 5, + fillOpacity: 0.2, + }); + const whiteColor = whiteOptionsBase({ + fillOpacity: 0.5, + weight: 5, + dashArray: '10 4 10', + }); + + const pathOptions = useContrast ? whiteColor : blueColor; + const renderData = isDataValid(showMobilityResults, filteredPostCodes); + const areResultsValid = isDataValid(showMobilityResults, mobilityProfilesData); + const renderMarkers = renderData && areResultsValid; + + /** + * Get icon by result value which is either object or in some cases 1. + * @param {object} resultValue + * @returns Leaflet icon + */ + const getIconByResult = resultValue => { + const isObject = typeof resultValue; + const result = isObject ? resultValue.result : 1; + switch (result) { + case 1: + return icon(createIcon(useContrast ? mooseContrast : moose)); + case 2: + return icon(createIcon(useContrast ? foxContrast : fox)); + case 3: + return icon(createIcon(useContrast ? rabbitContrast : rabbit)); + case 4: + return icon(createIcon(useContrast ? martenContrast : marten)); + case 5: + return icon(createIcon(useContrast ? deerContrast : deer)); + case 6: + return icon(createIcon(useContrast ? capercaillieContrast : capercaillie)); + default: + return icon(createIcon(useContrast ? mooseContrast : moose)); + } + }; + + /** + * Filter data to be contain results of only that specific postal code area. + * Get highest count value and call function to set icon. + * @param {string} nameValue + * @param {array} mobilityProfiles + * @returns leaflet icon + */ + const getCorrectIcon = (nameValue, mobilityProfiles) => { + const filteredMobilityProfiles = mobilityProfiles?.filter(item => item.postal_code_string === nameValue); + const maxCount = filteredMobilityProfiles?.reduce( + (prev, current) => (prev.count > current.count ? prev : current), + 1, + ); + return getIconByResult(maxCount); + }; + + /** + * Get center coordinates of the polygon shape. + * @param {array} coordinates + * @returns object + */ + const getCenter = coordinates => { + const leafletPolygon = polygon(coordinates); + return leafletPolygon.getBounds().getCenter(); + }; + + /** + * Swap coordinates and then get center value of the polygon. + * @param {array} coordinatesData + * @returns array of lat and lng values + */ + const swapAndGetCoordinates = coordinatesData => { + const swapped = swapCoords(coordinatesData); + const center = getCenter(swapped); + return [center.lat, center.lng]; + }; + + useEffect(() => { + if (renderData) { + const bounds = []; + filteredPostCodes.forEach(item => { + bounds.push(swapCoords(item.boundary.coordinates)); + }); + map.fitBounds(bounds); + } + }, [renderData, filteredPostCodes, map]); + + const renderMarkersData = (showData, data) => (showData + ? data.map(item => ( + + + + + + + + + + )) + : null); + + const renderPostCodeAreas = (showData, data) => (showData + ? data.map(item => ( + + + + + + + + + + )) + : null); + + return ( + <> + {renderMarkersData(renderMarkers, filteredPostCodes)} + {renderPostCodeAreas(renderData, filteredPostCodes)} + + ); +}; + +export default MobilityProfiles; diff --git a/src/components/MobilityPlatform/MobilityProfiles/components/MobilityProfilesContent/MobilityProfilesContent.js b/src/components/MobilityPlatform/MobilityProfiles/components/MobilityProfilesContent/MobilityProfilesContent.js new file mode 100644 index 000000000..b97af896b --- /dev/null +++ b/src/components/MobilityPlatform/MobilityProfiles/components/MobilityProfilesContent/MobilityProfilesContent.js @@ -0,0 +1,115 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { Typography } from '@mui/material'; +import { useIntl } from 'react-intl'; +import { ReactSVG } from 'react-svg'; +import { css } from '@emotion/css'; +import moose from 'servicemap-ui-turku/assets/icons/contrast/icons-icon_moose-bw-v2.svg'; +import fox from 'servicemap-ui-turku/assets/icons/contrast/icons-icon_fox-bw-v2.svg'; +import deer from 'servicemap-ui-turku/assets/icons/contrast/icons-icon_deer-bw-v2.svg'; +import rabbit from 'servicemap-ui-turku/assets/icons/contrast/icons-icon_rabbit-bw-v2.svg'; +import marten from 'servicemap-ui-turku/assets/icons/contrast/icons-icon_marten-bw-v2.svg'; +import capercaillie from 'servicemap-ui-turku/assets/icons/contrast/icons-icon_capercaillie-bw-v2.svg'; +import useLocaleText from '../../../../../utils/useLocaleText'; +import { + StyledContainer, + StyledFlexContainer, + StyledHeaderContainer, + StyledTextContainer, +} from '../../../styled/styled'; + +const MobilityProfilesContent = ({ postcodeArea, mobilityProfiles }) => { + const intl = useIntl(); + const getLocaleText = useLocaleText(); + + const filteredMobilityProfiles = mobilityProfiles.filter( + item => item.postal_code_string === postcodeArea.name.fi && item.postal_code_type_string === 'Home' && item.count >= 1, + ); + + const sortByCount = data => data.slice().sort((a, b) => b.count - a.count); + const sortedMobilityProfiles = sortByCount(filteredMobilityProfiles); + + const iconClass = css({ + width: '30px', + height: '30px', + }); + + const getIconByTopic = resultNum => { + switch (resultNum) { + case 1: + return moose; + case 2: + return fox; + case 3: + return rabbit; + case 4: + return marten; + case 5: + return deer; + case 6: + return capercaillie; + default: + return moose; + } + }; + + const noResultsText = () => ( + + + {intl.formatMessage({ id: 'area.mobilityResults.empty' })} + + + ); + + return ( + + + + {intl.formatMessage({ id: 'area.mobilityResults.postCodeArea' }, { value: postcodeArea.name.fi })} + + + {sortedMobilityProfiles?.length + ? sortedMobilityProfiles.map(item => ( + + + + + + + {getLocaleText(item.result_topics)} + + + + + {item.count} + + + + )) + : noResultsText()} + + ); +}; + +MobilityProfilesContent.propTypes = { + postcodeArea: PropTypes.shape({ + id: PropTypes.number, + name: PropTypes.shape({ + fi: PropTypes.string, + }), + }), + mobilityProfiles: PropTypes.arrayOf( + PropTypes.shape({ + postal_code_string: PropTypes.string, + postal_code_type_string: PropTypes.string, + result: PropTypes.number, + }), + ), +}; + +MobilityProfilesContent.defaultProps = { + postcodeArea: {}, + mobilityProfiles: [], +}; + +export default MobilityProfilesContent; diff --git a/src/components/MobilityPlatform/MobilityProfiles/components/MobilityProfilesContent/__tests__/MobilityProfilesContent.test.js b/src/components/MobilityPlatform/MobilityProfiles/components/MobilityProfilesContent/__tests__/MobilityProfilesContent.test.js new file mode 100644 index 000000000..1909fafef --- /dev/null +++ b/src/components/MobilityPlatform/MobilityProfiles/components/MobilityProfilesContent/__tests__/MobilityProfilesContent.test.js @@ -0,0 +1,52 @@ +// Link.react.test.js +import React from 'react'; +import MobilityProfilesContent from '../index'; +import { initialState } from '../../../../../../redux/reducers/user'; +import finnishTranslations from '../../../../../../i18n/fi'; +import { getRenderWithProviders } from '../../../../../../../jestUtils'; + +const mockProps = { + postcodeArea: { + name: { + en: '20210', + fi: '20210', + sv: '20210', + }, + }, + mobilityProfiles: [ + { + postal_code_string: '20210', + postal_code_type_string: 'Home', + result: 3, + result_topics: { + fi: 'Joustava Jänis', + sv: 'Hinderfri Hare', + en: 'Hassle-free Hare', + }, + count: 10, + }, + ], +}; + +const renderWithProviders = getRenderWithProviders({ + user: initialState, +}); + +describe('', () => { + it('should match snapshot', () => { + const { container } = renderWithProviders(); + expect(container).toMatchSnapshot(); + }); + + it('does show text correctly', () => { + const { container } = renderWithProviders(); + + const p = container.querySelectorAll('p'); + expect(p[0].textContent).toContain(`${finnishTranslations['area.mobilityResults.postCodeArea'].replace( + '{value}', + mockProps.postcodeArea.name.fi, + )}`); + expect(p[1].textContent).toContain(mockProps.mobilityProfiles[0].result_topics.fi); + expect(p[2].textContent).toContain(mockProps.mobilityProfiles[0].count); + }); +}); diff --git a/src/components/MobilityPlatform/MobilityProfiles/components/MobilityProfilesContent/__tests__/__snapshots__/MobilityProfilesContent.test.js.snap b/src/components/MobilityPlatform/MobilityProfiles/components/MobilityProfilesContent/__tests__/__snapshots__/MobilityProfilesContent.test.js.snap new file mode 100644 index 000000000..d714da549 --- /dev/null +++ b/src/components/MobilityPlatform/MobilityProfiles/components/MobilityProfilesContent/__tests__/__snapshots__/MobilityProfilesContent.test.js.snap @@ -0,0 +1,55 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[` should match snapshot 1`] = ` +
    +
    +
    +

    + Postinumeroalue: 20210 +

    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    + Joustava Jänis +

    +
    +
    +

    + 10 +

    +
    +
    +
    +
    +`; diff --git a/src/components/MobilityPlatform/MobilityProfiles/components/MobilityProfilesContent/index.js b/src/components/MobilityPlatform/MobilityProfiles/components/MobilityProfilesContent/index.js new file mode 100644 index 000000000..b7a623e67 --- /dev/null +++ b/src/components/MobilityPlatform/MobilityProfiles/components/MobilityProfilesContent/index.js @@ -0,0 +1,3 @@ +import MobilityProfilesContent from './MobilityProfilesContent'; + +export default MobilityProfilesContent; diff --git a/src/components/MobilityPlatform/MobilityProfiles/index.js b/src/components/MobilityPlatform/MobilityProfiles/index.js new file mode 100644 index 000000000..a47278b3f --- /dev/null +++ b/src/components/MobilityPlatform/MobilityProfiles/index.js @@ -0,0 +1,3 @@ +import MobilityProfiles from './MobilityProfiles'; + +export default MobilityProfiles; diff --git a/src/components/MobilityPlatform/OutdoorGymDevices/components/OutdoorGymDevicesContent/OutdoorGymDevicesContent.js b/src/components/MobilityPlatform/OutdoorGymDevices/components/OutdoorGymDevicesContent/OutdoorGymDevicesContent.js index f79a6068f..55d5ca764 100644 --- a/src/components/MobilityPlatform/OutdoorGymDevices/components/OutdoorGymDevicesContent/OutdoorGymDevicesContent.js +++ b/src/components/MobilityPlatform/OutdoorGymDevices/components/OutdoorGymDevicesContent/OutdoorGymDevicesContent.js @@ -1,8 +1,9 @@ import PropTypes from 'prop-types'; import React from 'react'; +import { StyledContainer, StyledHeaderContainer } from '../../../styled/styled'; import TextComponent from '../../../TextComponent'; -const OutdoorGymDevicesContent = ({ classes, item }) => { +const OutdoorGymDevicesContent = ({ item }) => { const deviceName = { fi: item.name_fi, en: item.name_en, @@ -22,21 +23,30 @@ const OutdoorGymDevicesContent = ({ classes, item }) => { }; return ( -
    -
    + + -
    -
    + +
    {item.address_fi !== '' ? : null}
    -
    + ); }; OutdoorGymDevicesContent.propTypes = { - classes: PropTypes.objectOf(PropTypes.any).isRequired, - item: PropTypes.objectOf(PropTypes.any), + item: PropTypes.shape({ + address_fi: PropTypes.string, + address_en: PropTypes.string, + address_sv: PropTypes.string, + name_fi: PropTypes.string, + name_en: PropTypes.string, + name_sv: PropTypes.string, + description_fi: PropTypes.string, + description_en: PropTypes.string, + description_sv: PropTypes.string, + }), }; OutdoorGymDevicesContent.defaultProps = { diff --git a/src/components/MobilityPlatform/OutdoorGymDevices/components/OutdoorGymDevicesContent/__tests__/__snapshots__/OutdoorGymDevicesContent.test.js.snap b/src/components/MobilityPlatform/OutdoorGymDevices/components/OutdoorGymDevicesContent/__tests__/__snapshots__/OutdoorGymDevicesContent.test.js.snap index c75d55db3..82495c953 100644 --- a/src/components/MobilityPlatform/OutdoorGymDevices/components/OutdoorGymDevicesContent/__tests__/__snapshots__/OutdoorGymDevicesContent.test.js.snap +++ b/src/components/MobilityPlatform/OutdoorGymDevices/components/OutdoorGymDevicesContent/__tests__/__snapshots__/OutdoorGymDevicesContent.test.js.snap @@ -3,13 +3,13 @@ exports[` should match snapshot 1`] = `

    should match snapshot 1`] = `

    -
    +

    should match snapshot 1`] = `

    ({ - container: { - margin: theme.spacing(1), - }, - headerContainer: { - width: '85%', - borderBottom: '1px solid #000', - paddingBottom: theme.spacing(0.5), - }, - textContainer: { - marginTop: theme.spacing(0.5), - }, - contentInner: { - marginLeft: theme.spacing(1), - marginBottom: theme.spacing(1), - }, - margin: { - margin: theme.spacing(0.4), - }, - marginTop: { - marginTop: theme.spacing(0.4), - }, -}); diff --git a/src/components/MobilityPlatform/ParkAndRideStops/ParkAndRideBikes/components/ParkAndRideBikesContent/ParkAndRideBikesContent.js b/src/components/MobilityPlatform/ParkAndRideStops/ParkAndRideBikes/components/ParkAndRideBikesContent/ParkAndRideBikesContent.js index 1f8c0e682..32405b8e1 100644 --- a/src/components/MobilityPlatform/ParkAndRideStops/ParkAndRideBikes/components/ParkAndRideBikesContent/ParkAndRideBikesContent.js +++ b/src/components/MobilityPlatform/ParkAndRideStops/ParkAndRideBikes/components/ParkAndRideBikesContent/ParkAndRideBikesContent.js @@ -1,8 +1,8 @@ import PropTypes from 'prop-types'; import React from 'react'; -import styled from '@emotion/styled'; import { Typography } from '@mui/material'; import { useIntl } from 'react-intl'; +import { StyledContainer, StyledHeaderContainer, StyledTextContainer } from '../../../../styled/styled'; import TextComponent from '../../../../TextComponent'; const ParkAndRideBikesContent = ({ item }) => { @@ -36,40 +36,22 @@ const ParkAndRideBikesContent = ({ item }) => { - - +

    + {intl.formatMessage({ id: 'mobilityPlatform.parkAndRide.content.subtitle' })} - + {item.address_fi !== '' ? ( ) : null} - +
    ); return {parkAndRideInfo}; }; -const StyledContainer = styled.div(({ theme }) => ({ - margin: theme.spacing(1), -})); - -const StyledHeaderContainer = styled.div(({ theme }) => ({ - width: '85%', - borderBottom: '1px solid #000', - paddingBottom: theme.spacing(0.5), -})); - -const StyledTextContainer = styled.div(({ theme }) => ({ - marginTop: theme.spacing(0.5), -})); - -const StyledMargin = styled.div(({ theme }) => ({ - margin: theme.spacing(0.4), -})); - ParkAndRideBikesContent.propTypes = { item: PropTypes.shape({ name_fi: PropTypes.string, diff --git a/src/components/MobilityPlatform/ParkAndRideStops/ParkAndRideBikes/components/ParkAndRideBikesContent/__tests__/__snapshots__/ParkAndRideBikesContent.test.js.snap b/src/components/MobilityPlatform/ParkAndRideStops/ParkAndRideBikes/components/ParkAndRideBikesContent/__tests__/__snapshots__/ParkAndRideBikesContent.test.js.snap index 2cfb04334..7fefbc7b6 100644 --- a/src/components/MobilityPlatform/ParkAndRideStops/ParkAndRideBikes/components/ParkAndRideBikesContent/__tests__/__snapshots__/ParkAndRideBikesContent.test.js.snap +++ b/src/components/MobilityPlatform/ParkAndRideStops/ParkAndRideBikes/components/ParkAndRideBikesContent/__tests__/__snapshots__/ParkAndRideBikesContent.test.js.snap @@ -12,7 +12,7 @@ exports[` should work 1`] = ` class="css-1dznbe2" >

    should work 1`] = `

    -
    +

    should work 1`] = `

    { +const DisabledParkingContent = ({ item }) => { + const intl = useIntl(); const getLocaleText = useLocaleText(); - const renderAccessInfo = (accessValue) => { + const renderAccessInfo = accessValue => { const accessValueLower = accessValue.toLowerCase(); if (accessValueLower === 'vapaa paasy') { return ( - - {intl.formatMessage({ id: 'mobilityPlatform.content.publicParking.access' })} - + + + {intl.formatMessage({ id: 'mobilityPlatform.content.publicParking.access' })} + + ); } if (accessValueLower === 'portti') { return ( - - {intl.formatMessage({ id: 'mobilityPlatform.content.publicParking.access.gate' })} - + + + {intl.formatMessage({ id: 'mobilityPlatform.content.publicParking.access.gate' })} + + ); } return null; }; - const renderText = (msgId, value, props = {}) => ( -

    + const renderText = (msgId, value) => ( + {intl.formatMessage({ id: msgId }, { value })} -
    + ); const parkingAreaAddress = { @@ -37,33 +44,38 @@ const DisabledParkingContent = ({ classes, intl, item }) => { sv: item.address_sv, }; - const renderAddress = () => renderText('mobilityPlatform.content.address', getLocaleText(parkingAreaAddress), { className: classes.margin }); + const renderAddress = () => renderText('mobilityPlatform.content.address', getLocaleText(parkingAreaAddress)); return ( -
    -
    + + {intl.formatMessage({ id: 'mobilityPlatform.content.disabledParking.title' })} -
    -
    + +
    {renderAddress()} - {renderText('mobilityPlatform.content.disabledParking.amount', item.extra.invapaikkoja, { - className: classes.margin, - })} -
    + {renderText('mobilityPlatform.content.disabledParking.amount', item.extra.invapaikkoja)} + {getLocaleText(item.extra.rajoitustyyppi)} -
    -
    {renderAccessInfo(item.extra.saavutettavuus.fi)}
    + + {renderAccessInfo(item.extra.saavutettavuus.fi)}
    -
    + ); }; DisabledParkingContent.propTypes = { - classes: PropTypes.objectOf(PropTypes.any).isRequired, - intl: PropTypes.objectOf(PropTypes.any).isRequired, - item: PropTypes.objectOf(PropTypes.any), + item: PropTypes.shape({ + address_fi: PropTypes.string, + address_en: PropTypes.string, + address_sv: PropTypes.string, + extra: PropTypes.shape({ + invapaikkoja: PropTypes.number, + rajoitustyyppi: PropTypes.objectOf(PropTypes.string), + saavutettavuus: PropTypes.objectOf(PropTypes.string), + }), + }), }; DisabledParkingContent.defaultProps = { diff --git a/src/components/MobilityPlatform/Parking/DisabledParking/components/DisabledParkingContent/__tests__/__snapshots__/DisabledParkingContent.test.js.snap b/src/components/MobilityPlatform/Parking/DisabledParking/components/DisabledParkingContent/__tests__/__snapshots__/DisabledParkingContent.test.js.snap index 4da4f4016..a9816bdef 100644 --- a/src/components/MobilityPlatform/Parking/DisabledParking/components/DisabledParkingContent/__tests__/__snapshots__/DisabledParkingContent.test.js.snap +++ b/src/components/MobilityPlatform/Parking/DisabledParking/components/DisabledParkingContent/__tests__/__snapshots__/DisabledParkingContent.test.js.snap @@ -3,10 +3,10 @@ exports[` does match snapshot 1`] = `

    does match snapshot 1`] = ` Pysäköintialue liikkumisesteisille

    -
    +

    does match snapshot 1`] = `

    does match snapshot 1`] = `

    does match snapshot 1`] = `

    ({ - container: { - margin: theme.spacing(1), - }, - headerContainer: { - width: '85%', - borderBottom: '1px solid #000', - paddingBottom: theme.spacing(0.5), - }, - textContainer: { - marginTop: theme.spacing(0.5), - }, - margin: { - margin: theme.spacing(0.4), - }, -}); diff --git a/src/components/MobilityPlatform/Parking/PublicParking/components/PublicParkingContent/PublicParkingContent.js b/src/components/MobilityPlatform/Parking/PublicParking/components/PublicParkingContent/PublicParkingContent.js index f4480ebcb..654c865dc 100644 --- a/src/components/MobilityPlatform/Parking/PublicParking/components/PublicParkingContent/PublicParkingContent.js +++ b/src/components/MobilityPlatform/Parking/PublicParking/components/PublicParkingContent/PublicParkingContent.js @@ -1,18 +1,22 @@ import { Typography } from '@mui/material'; import PropTypes from 'prop-types'; import React from 'react'; +import { useIntl } from 'react-intl'; +import { StyledContainer, StyledHeaderContainer, StyledTextContainer } from '../../../../styled/styled'; import TextComponent from '../../../../TextComponent'; -const PublicParkingContent = ({ classes, intl, item }) => { +const PublicParkingContent = ({ item }) => { + const intl = useIntl(); + const renderText = (msgId, value) => ( -

    + {value ? intl.formatMessage({ id: msgId }, { value }) : intl.formatMessage({ id: msgId })} -
    + ); - const renderAccessInfo = (accessValue) => { + const renderAccessInfo = accessValue => { const accessValueLower = accessValue.toLowerCase(); if (accessValueLower === 'vapaa paasy') { return renderText('mobilityPlatform.content.publicParking.access'); @@ -44,11 +48,11 @@ const PublicParkingContent = ({ classes, intl, item }) => { }; return ( -
    -
    + + -
    -
    + +
    {renderText(translations.placesTotal, item.extra.paikkoja_y)} {item.extra.max_aika_h ? renderText(translations.totalTime, item.extra.max_aika_h) : null} @@ -56,14 +60,26 @@ const PublicParkingContent = ({ classes, intl, item }) => { {item.extra.rajoit_lisat ? : null} {renderAccessInfo(item.extra.saavutettavuus.fi)}
    -
    + ); }; PublicParkingContent.propTypes = { - classes: PropTypes.objectOf(PropTypes.any).isRequired, - intl: PropTypes.objectOf(PropTypes.any).isRequired, - item: PropTypes.objectOf(PropTypes.any), + item: PropTypes.shape({ + address_fi: PropTypes.string, + address_en: PropTypes.string, + address_sv: PropTypes.string, + name_fi: PropTypes.string, + name_en: PropTypes.string, + name_sv: PropTypes.string, + extra: PropTypes.shape({ + paikkoja_y: PropTypes.number, + max_aika_h: PropTypes.number, + rajoitustyyppi: PropTypes.objectOf(PropTypes.string), + rajoit_lisat: PropTypes.objectOf(PropTypes.string), + saavutettavuus: PropTypes.objectOf(PropTypes.string), + }), + }), }; PublicParkingContent.defaultProps = { diff --git a/src/components/MobilityPlatform/Parking/PublicParking/components/PublicParkingContent/__tests__/__snapshots__/PublicParkingContent.test.js.snap b/src/components/MobilityPlatform/Parking/PublicParking/components/PublicParkingContent/__tests__/__snapshots__/PublicParkingContent.test.js.snap index 4577ed7d5..68b8a3928 100644 --- a/src/components/MobilityPlatform/Parking/PublicParking/components/PublicParkingContent/__tests__/__snapshots__/PublicParkingContent.test.js.snap +++ b/src/components/MobilityPlatform/Parking/PublicParking/components/PublicParkingContent/__tests__/__snapshots__/PublicParkingContent.test.js.snap @@ -3,13 +3,13 @@ exports[` does match snapshot 1`] = `

    does match snapshot 1`] = `

    -
    +

    does match snapshot 1`] = `

    does match snapshot 1`] = `

    does match snapshot 1`] = `

    does match snapshot 1`] = `

    does match snapshot 1`] = `

    ({ - container: { - margin: theme.spacing(1), - }, - headerContainer: { - width: '85%', - borderBottom: '1px solid #000', - paddingBottom: theme.spacing(0.5), - }, - textContainer: { - marginTop: theme.spacing(0.5), - }, - margin: { - margin: theme.spacing(0.4), - }, -}); diff --git a/src/components/MobilityPlatform/Parking/RentalCarParking/components/RentalCarParkingContent/RentalCarParkingContent.js b/src/components/MobilityPlatform/Parking/RentalCarParking/components/RentalCarParkingContent/RentalCarParkingContent.js index 7c68228dd..5027db54e 100644 --- a/src/components/MobilityPlatform/Parking/RentalCarParking/components/RentalCarParkingContent/RentalCarParkingContent.js +++ b/src/components/MobilityPlatform/Parking/RentalCarParking/components/RentalCarParkingContent/RentalCarParkingContent.js @@ -1,20 +1,24 @@ import { Typography } from '@mui/material'; import PropTypes from 'prop-types'; import React from 'react'; +import { useIntl } from 'react-intl'; +import { StyledContainer, StyledHeaderContainer, StyledTextContainer } from '../../../../styled/styled'; import TextComponent from '../../../../TextComponent'; -const RentalCarParkingContent = ({ classes, intl, item }) => { +const RentalCarParkingContent = ({ item }) => { + const intl = useIntl(); + const renderText = (msgId, value) => ( -

    + {value ? intl.formatMessage({ id: msgId }, { value }) : intl.formatMessage({ id: msgId })} -
    + ); const formatName = name => name?.split(',')[0]; - const renderAccessInfo = (accessValue) => { + const renderAccessInfo = accessValue => { const accessValueLower = accessValue.toLowerCase(); if (accessValueLower === 'vapaa pääsy') { return renderText('mobilityPlatform.content.publicParking.access'); @@ -40,25 +44,36 @@ const RentalCarParkingContent = ({ classes, intl, item }) => { }; return ( -
    -
    + + -
    -
    + +
    {renderText(translations.placesTotal, item.extra.Paikkoja_y)} {item.extra.Max_aika_h ? renderText(translations.totalTime, item.extra.Max_aika_h) : null} {item.extra.Rajoit_lis ? : null} {renderAccessInfo(item.extra.Saavutetta)}
    -
    + ); }; RentalCarParkingContent.propTypes = { - classes: PropTypes.objectOf(PropTypes.any).isRequired, - intl: PropTypes.objectOf(PropTypes.any).isRequired, - item: PropTypes.objectOf(PropTypes.any), + item: PropTypes.shape({ + address_fi: PropTypes.string, + address_en: PropTypes.string, + address_sv: PropTypes.string, + name_fi: PropTypes.string, + name_en: PropTypes.string, + name_sv: PropTypes.string, + extra: PropTypes.shape({ + Paikkoja_y: PropTypes.string, + Max_aika_h: PropTypes.number, + Rajoit_lis: PropTypes.objectOf(PropTypes.string), + Saavutetta: PropTypes.objectOf(PropTypes.string), + }), + }), }; RentalCarParkingContent.defaultProps = { diff --git a/src/components/MobilityPlatform/Parking/RentalCarParking/components/RentalCarParkingContent/__tests__/__snapshots__/RentalCarParkingContent.test.js.snap b/src/components/MobilityPlatform/Parking/RentalCarParking/components/RentalCarParkingContent/__tests__/__snapshots__/RentalCarParkingContent.test.js.snap index 4e5c2f643..f2d554f7a 100644 --- a/src/components/MobilityPlatform/Parking/RentalCarParking/components/RentalCarParkingContent/__tests__/__snapshots__/RentalCarParkingContent.test.js.snap +++ b/src/components/MobilityPlatform/Parking/RentalCarParking/components/RentalCarParkingContent/__tests__/__snapshots__/RentalCarParkingContent.test.js.snap @@ -3,13 +3,13 @@ exports[` does match snapshot 1`] = `

    does match snapshot 1`] = `

    -
    +

    does match snapshot 1`] = `

    does match snapshot 1`] = `

    does match snapshot 1`] = `

    does match snapshot 1`] = `

    ({ - container: { - margin: theme.spacing(1), - }, - headerContainer: { - width: '85%', - borderBottom: '1px solid #000', - paddingBottom: theme.spacing(0.5), - }, - textContainer: { - marginTop: theme.spacing(0.5), - }, - margin: { - margin: theme.spacing(0.4), - }, -}); diff --git a/src/components/MobilityPlatform/ParkingChargeZones/components/ParkingChargeZoneContent/ParkingChargeZoneContent.js b/src/components/MobilityPlatform/ParkingChargeZones/components/ParkingChargeZoneContent/ParkingChargeZoneContent.js index f5429c2fa..851b82646 100644 --- a/src/components/MobilityPlatform/ParkingChargeZones/components/ParkingChargeZoneContent/ParkingChargeZoneContent.js +++ b/src/components/MobilityPlatform/ParkingChargeZones/components/ParkingChargeZoneContent/ParkingChargeZoneContent.js @@ -1,40 +1,77 @@ import { Typography } from '@mui/material'; import PropTypes from 'prop-types'; import React from 'react'; +import { useIntl } from 'react-intl'; +import styled from '@emotion/styled'; +import { StyledHeaderContainer, StyledTextContainer } from '../../../styled/styled'; -const ParkingChargeZoneContent = ({ classes, intl, parkingChargeZone }) => { - const renderText = (msgId, objValue, isTitle) => ( -

    - - {intl.formatMessage({ - id: msgId, - })} - : - {' '} - {objValue} +const ParkingChargeZoneContent = ({ parkingChargeZone }) => { + const intl = useIntl(); + + const renderTitle = (msgId, objValue) => ( + + + {intl.formatMessage( + { + id: msgId, + }, + { value: objValue }, + )} -
    + + ); + + const renderText = (msgId, objValue) => ( + + + {intl.formatMessage( + { + id: msgId, + }, + { value: objValue }, + )} + + ); return ( -
    - {renderText('mobilityPlatform.content.parkingChargeZones.zone', parkingChargeZone.extra.maksuvyohyke, true)} - {renderText('mobilityPlatform.content.parkingChargeZones.price.weekDays', parkingChargeZone.extra.maksullisuus_arki, false)} - {renderText('mobilityPlatform.content.parkingChargeZones.price.saturday', parkingChargeZone.extra.maksullisuus_lauantai, false)} - {renderText('mobilityPlatform.content.parkingChargeZones.price.sunday', parkingChargeZone.extra.maksullisuus_sunnuntai, false)} - {renderText('mobilityPlatform.content.parkingChargeZones.price', parkingChargeZone.extra.maksuvyohykehinta, false)} -
    + + {renderTitle('mobilityPlatform.content.parkingChargeZones.zone', parkingChargeZone.extra.maksuvyohyke)} + {renderText( + 'mobilityPlatform.content.parkingChargeZones.price.weekDays', + parkingChargeZone.extra.maksullisuus_arki, + )} + {renderText( + 'mobilityPlatform.content.parkingChargeZones.price.saturday', + parkingChargeZone.extra.maksullisuus_lauantai, + )} + {renderText( + 'mobilityPlatform.content.parkingChargeZones.price.sunday', + parkingChargeZone.extra.maksullisuus_sunnuntai, + )} + {renderText('mobilityPlatform.content.parkingChargeZones.price', parkingChargeZone.extra.maksuvyohykehinta)} + ); }; +const StyledPadding = styled.div(({ theme }) => ({ + padding: theme.spacing(1), +})); + ParkingChargeZoneContent.propTypes = { - classes: PropTypes.objectOf(PropTypes.any).isRequired, - intl: PropTypes.objectOf(PropTypes.any).isRequired, - parkingChargeZone: PropTypes.objectOf(PropTypes.any), + parkingChargeZone: PropTypes.shape({ + extra: PropTypes.shape({ + maksuvyohyke: PropTypes.string, + maksullisuus_arki: PropTypes.string, + maksullisuus_lauantai: PropTypes.string, + maksullisuus_sunnuntai: PropTypes.string, + maksuvyohykehinta: PropTypes.string, + }), + }), }; ParkingChargeZoneContent.defaultProps = { - parkingChargeZone: null, + parkingChargeZone: {}, }; export default ParkingChargeZoneContent; diff --git a/src/components/MobilityPlatform/ParkingChargeZones/components/ParkingChargeZoneContent/__tests__/ParkingChargeZoneContent.test.js b/src/components/MobilityPlatform/ParkingChargeZones/components/ParkingChargeZoneContent/__tests__/ParkingChargeZoneContent.test.js index 5b4810fb0..2af5e1b55 100644 --- a/src/components/MobilityPlatform/ParkingChargeZones/components/ParkingChargeZoneContent/__tests__/ParkingChargeZoneContent.test.js +++ b/src/components/MobilityPlatform/ParkingChargeZones/components/ParkingChargeZoneContent/__tests__/ParkingChargeZoneContent.test.js @@ -31,19 +31,34 @@ describe('', () => { const p = container.querySelectorAll('p'); const h3 = container.querySelector('h3'); expect(h3.textContent).toContain( - `${finnishTranslations['mobilityPlatform.content.parkingChargeZones.zone']}: ${mockProps.parkingChargeZone.extra.maksuvyohyke}`, + `${finnishTranslations['mobilityPlatform.content.parkingChargeZones.zone'].replace( + '{value}', + mockProps.parkingChargeZone.extra.maksuvyohyke, + )}`, ); expect(p[0].textContent).toContain( - `${finnishTranslations['mobilityPlatform.content.parkingChargeZones.price.weekDays']}: ${mockProps.parkingChargeZone.extra.maksullisuus_arki}`, + `${finnishTranslations['mobilityPlatform.content.parkingChargeZones.price.weekDays'].replace( + '{value}', + mockProps.parkingChargeZone.extra.maksullisuus_arki, + )}`, ); expect(p[1].textContent).toContain( - `${finnishTranslations['mobilityPlatform.content.parkingChargeZones.price.saturday']}: ${mockProps.parkingChargeZone.extra.maksullisuus_lauantai}`, + `${finnishTranslations['mobilityPlatform.content.parkingChargeZones.price.saturday'].replace( + '{value}', + mockProps.parkingChargeZone.extra.maksullisuus_lauantai, + )}`, ); expect(p[2].textContent).toContain( - `${finnishTranslations['mobilityPlatform.content.parkingChargeZones.price.sunday']}: ${mockProps.parkingChargeZone.extra.maksullisuus_sunnuntai}`, + `${finnishTranslations['mobilityPlatform.content.parkingChargeZones.price.sunday'].replace( + '{value}', + mockProps.parkingChargeZone.extra.maksullisuus_sunnuntai, + )}`, ); expect(p[3].textContent).toContain( - `${finnishTranslations['mobilityPlatform.content.parkingChargeZones.price']}: ${mockProps.parkingChargeZone.extra.maksuvyohykehinta}`, + `${finnishTranslations['mobilityPlatform.content.parkingChargeZones.price'].replace( + '{value}', + mockProps.parkingChargeZone.extra.maksuvyohykehinta, + )}`, ); }); }); diff --git a/src/components/MobilityPlatform/ParkingChargeZones/components/ParkingChargeZoneContent/__tests__/__snapshots__/ParkingChargeZoneContent.test.js.snap b/src/components/MobilityPlatform/ParkingChargeZones/components/ParkingChargeZoneContent/__tests__/__snapshots__/ParkingChargeZoneContent.test.js.snap index b1bad6106..7665c9ef9 100644 --- a/src/components/MobilityPlatform/ParkingChargeZones/components/ParkingChargeZoneContent/__tests__/__snapshots__/ParkingChargeZoneContent.test.js.snap +++ b/src/components/MobilityPlatform/ParkingChargeZones/components/ParkingChargeZoneContent/__tests__/__snapshots__/ParkingChargeZoneContent.test.js.snap @@ -3,58 +3,51 @@ exports[` should work 1`] = `

    - Vyöhyke - : - - 1 + Vyöhyke: 1

    -
    +

    - Maksullisuus arkisin - : - - 9 - 18 + Maksullisuus arkisin: 9 - 18

    -
    +

    - Maksullisuus lauantaisin - : - - 9 - 15 + Maksullisuus lauantaisin: 9 - 15

    -
    +

    - Maksullisuus sunnuntaisin - : - - Maksuton + Maksullisuus sunnuntaisin: Maksuton

    -
    +

    - Hinta - : - - 3 €/h + Hinta: 3 €/h

    diff --git a/src/components/MobilityPlatform/ParkingChargeZones/components/ParkingChargeZoneContent/index.js b/src/components/MobilityPlatform/ParkingChargeZones/components/ParkingChargeZoneContent/index.js index 106ef9f89..284290832 100644 --- a/src/components/MobilityPlatform/ParkingChargeZones/components/ParkingChargeZoneContent/index.js +++ b/src/components/MobilityPlatform/ParkingChargeZones/components/ParkingChargeZoneContent/index.js @@ -1,6 +1,3 @@ -import { withStyles } from '@mui/styles'; -import { injectIntl } from 'react-intl'; import ParkingChargeZoneContent from './ParkingChargeZoneContent'; -import styles from './styles'; -export default withStyles(styles)(injectIntl(ParkingChargeZoneContent)); +export default ParkingChargeZoneContent; diff --git a/src/components/MobilityPlatform/ParkingChargeZones/components/ParkingChargeZoneContent/styles.js b/src/components/MobilityPlatform/ParkingChargeZones/components/ParkingChargeZoneContent/styles.js deleted file mode 100644 index dcbb11119..000000000 --- a/src/components/MobilityPlatform/ParkingChargeZones/components/ParkingChargeZoneContent/styles.js +++ /dev/null @@ -1,11 +0,0 @@ -export default theme => ({ - padding: { - padding: theme.spacing(1), - }, - subtitle: { - marginBottom: theme.spacing(1), - paddingBottom: theme.spacing(0.5), - borderBottom: '1px solid #000000', - width: '88%', - }, -}); diff --git a/src/components/MobilityPlatform/ParkingMachines/ParkingMachines.js b/src/components/MobilityPlatform/ParkingMachines/ParkingMachines.js index b7d7bc246..0f566fef6 100644 --- a/src/components/MobilityPlatform/ParkingMachines/ParkingMachines.js +++ b/src/components/MobilityPlatform/ParkingMachines/ParkingMachines.js @@ -14,7 +14,7 @@ import ParkingMachinesContent from './components/ParkingMachinesContent'; const ParkingMachines = () => { const options = { type_name: 'ParkingMachine', - page_size: 500, + page_size: 200, }; const { showParkingMachines } = useMobilityPlatformContext(); diff --git a/src/components/MobilityPlatform/ParkingMachines/components/ParkingMachinesContent/ParkingMachinesContent.js b/src/components/MobilityPlatform/ParkingMachines/components/ParkingMachinesContent/ParkingMachinesContent.js index 666332dc9..3b133778e 100644 --- a/src/components/MobilityPlatform/ParkingMachines/components/ParkingMachinesContent/ParkingMachinesContent.js +++ b/src/components/MobilityPlatform/ParkingMachines/components/ParkingMachinesContent/ParkingMachinesContent.js @@ -1,16 +1,20 @@ import { Typography } from '@mui/material'; import PropTypes from 'prop-types'; import React from 'react'; +import { useIntl } from 'react-intl'; +import { StyledContainer, StyledHeaderContainer, StyledTextContainer } from '../../../styled/styled'; import TextComponent from '../../../TextComponent'; -const ParkingMachinesContent = ({ classes, intl, item }) => { +const ParkingMachinesContent = ({ item }) => { + const intl = useIntl(); + /** For values that are not objects and do not contain localized strings */ const singleValText = (messageId, value) => ( -
    + {intl.formatMessage({ id: messageId }, { value })} -
    + ); const machineAddress = { @@ -19,36 +23,56 @@ const ParkingMachinesContent = ({ classes, intl, item }) => { sv: item.address_sv, }; - const formatPrice = price => price.toString().replace('.', ','); + const paymentType = { + fi: item.extra.maksutapa_fi, + en: item.extra.maksutapa_en, + sv: item.extra.maksutapa_sv, + }; + + const otherInfo = { + fi: item.extra.muu_tieto_fi, + en: item.extra.muu_tieto_en, + sv: item.extra.muu_tieto_sv, + }; const parkingMachineInfo = ( -
    -
    + + {intl.formatMessage({ id: 'mobilityPlatform.content.parkingMachine.title' })} + +
    + {item?.address_fi ? : null} + {singleValText('mobilityPlatform.content.parkingMachine.payment', item?.extra?.taksa)} + + {item?.extra?.muu_tieto_fi ? : null}
    -
    - {item.address_fi ? : null} - - {singleValText('mobilityPlatform.content.parkingMachine.payment', formatPrice(item.extra['Taksa/h']))} - - {item.extra.Muuta ? singleValText('mobilityPlatform.content.parkingMachine.otherInfo', item.extra.Muuta) : null} -
    -
    + ); return ( -
    +
    {parkingMachineInfo}
    ); }; ParkingMachinesContent.propTypes = { - classes: PropTypes.objectOf(PropTypes.any).isRequired, - intl: PropTypes.objectOf(PropTypes.any).isRequired, - item: PropTypes.objectOf(PropTypes.any), + item: PropTypes.shape({ + address_fi: PropTypes.string, + address_en: PropTypes.string, + address_sv: PropTypes.string, + extra: PropTypes.shape({ + maksutapa_fi: PropTypes.string, + maksutapa_en: PropTypes.string, + maksutapa_sv: PropTypes.string, + taksa: PropTypes.string, + muu_tieto_fi: PropTypes.string, + muu_tieto_en: PropTypes.string, + muu_tieto_sv: PropTypes.string, + }), + }), }; ParkingMachinesContent.defaultProps = { diff --git a/src/components/MobilityPlatform/ParkingMachines/components/ParkingMachinesContent/__tests__/ParkingMachinesContent.test.js b/src/components/MobilityPlatform/ParkingMachines/components/ParkingMachinesContent/__tests__/ParkingMachinesContent.test.js index f79e10ec0..fa4d8c86c 100644 --- a/src/components/MobilityPlatform/ParkingMachines/components/ParkingMachinesContent/__tests__/ParkingMachinesContent.test.js +++ b/src/components/MobilityPlatform/ParkingMachines/components/ParkingMachinesContent/__tests__/ParkingMachinesContent.test.js @@ -11,18 +11,13 @@ const mockProps = { address_en: 'address', address_sv: 'addres', extra: { - Sijainti: { - en: 'On-street', - fi: 'Katuosa', - sv: 'Gata', - }, - 'Taksa/h': '1.8', - Maksutapa: { - en: 'Coin, card, contactless', - fi: 'Kolikko, kortti, lähimaksu', - sv: 'Mynt, kort, kontaktlös', - }, - Muuta: 'Testiteksti', + taksa: '0,5 €/h', + maksutapa_en: 'Coin, card, contactless', + maksutapa_fi: 'Kolikko, kortti, lähimaksu', + maksutapa_sv: 'Mynt, kort, kontaktlös', + muu_tieto_en: 'Tariff 0,5 €/h first 8h, 0,2 €/h time over 8h', + muu_tieto_fi: 'Taksa 0,5€/h ensimmäiset 8h, 0,2€/h aika yli 8h', + muu_tieto_sv: 'Taxa 0,5 €/t första 8t, 0,2 €/t efter 8t', }, }, }; @@ -44,9 +39,8 @@ describe('', () => { const p = container.querySelectorAll('p'); expect(h3[0].textContent).toContain(finnishTranslations['mobilityPlatform.content.parkingMachine.title']); expect(p[0].textContent).toContain(`Osoite: ${mockProps.item.address_fi}`); - expect(p[1].textContent).toContain(`Sijainti: ${mockProps.item.extra.Sijainti.fi}`); - expect(p[2].textContent).toContain('Maksu: 1,8 €/t'); - expect(p[3].textContent).toContain(`Maksutavat: ${mockProps.item.extra.Maksutapa.fi}`); - expect(p[4].textContent).toContain(`Lisätietoja: ${mockProps.item.extra.Muuta}`); + expect(p[1].textContent).toContain('Maksu: 0,5 €/h'); + expect(p[2].textContent).toContain(`Maksutavat: ${mockProps.item.extra.maksutapa_fi}`); + expect(p[3].textContent).toContain(`Lisätietoja: ${mockProps.item.extra.muu_tieto_fi}`); }); }); diff --git a/src/components/MobilityPlatform/ParkingMachines/components/ParkingMachinesContent/__tests__/__snapshots__/ParkingMachinesContent.test.js.snap b/src/components/MobilityPlatform/ParkingMachines/components/ParkingMachinesContent/__tests__/__snapshots__/ParkingMachinesContent.test.js.snap index b13588729..961043d69 100644 --- a/src/components/MobilityPlatform/ParkingMachines/components/ParkingMachinesContent/__tests__/__snapshots__/ParkingMachinesContent.test.js.snap +++ b/src/components/MobilityPlatform/ParkingMachines/components/ParkingMachinesContent/__tests__/__snapshots__/ParkingMachinesContent.test.js.snap @@ -2,12 +2,12 @@ exports[` should match snapshot 1`] = `
    -
    -
    +
    +

    should match snapshot 1`] = ` Pysäköintiautomaatti

    -
    +

    should match snapshot 1`] = `

    -

    - Sijainti: Katuosa -

    -
    -

    - Maksu: 1,8 €/t + Maksu: 0,5 €/h

    should match snapshot 1`] = `

    - Lisätietoja: Testiteksti + Lisätietoja: Taksa 0,5€/h ensimmäiset 8h, 0,2€/h aika yli 8h

    diff --git a/src/components/MobilityPlatform/ParkingMachines/components/ParkingMachinesContent/index.js b/src/components/MobilityPlatform/ParkingMachines/components/ParkingMachinesContent/index.js index b5672661d..4137ca4b5 100644 --- a/src/components/MobilityPlatform/ParkingMachines/components/ParkingMachinesContent/index.js +++ b/src/components/MobilityPlatform/ParkingMachines/components/ParkingMachinesContent/index.js @@ -1,6 +1,3 @@ -import { withStyles } from '@mui/styles'; -import { injectIntl } from 'react-intl'; import ParkingMachinesContent from './ParkingMachinesContent'; -import styles from './styles'; -export default withStyles(styles)(injectIntl(ParkingMachinesContent)); +export default ParkingMachinesContent; diff --git a/src/components/MobilityPlatform/ParkingMachines/components/ParkingMachinesContent/styles.js b/src/components/MobilityPlatform/ParkingMachines/components/ParkingMachinesContent/styles.js deleted file mode 100644 index 0f6ab6ab0..000000000 --- a/src/components/MobilityPlatform/ParkingMachines/components/ParkingMachinesContent/styles.js +++ /dev/null @@ -1,16 +0,0 @@ -export default theme => ({ - padding: { - padding: theme.spacing(0.7), - }, - headerContainer: { - width: '85%', - borderBottom: '1px solid #000', - paddingBottom: theme.spacing(0.5), - }, - textContainer: { - marginTop: theme.spacing(0.5), - }, - margin: { - margin: theme.spacing(0.4), - }, -}); diff --git a/src/components/MobilityPlatform/ParkingSpaces/ParkingSpaces.js b/src/components/MobilityPlatform/ParkingSpaces/ParkingSpaces.js index f689d769d..d0ca58af4 100644 --- a/src/components/MobilityPlatform/ParkingSpaces/ParkingSpaces.js +++ b/src/components/MobilityPlatform/ParkingSpaces/ParkingSpaces.js @@ -2,7 +2,7 @@ import React, { useEffect, useState } from 'react'; import { useSelector } from 'react-redux'; import { useMap } from 'react-leaflet'; -import { fetchParkingAreaGeometries, fetchParkingAreaStats } from '../mobilityPlatformRequests/mobilityPlatformRequests'; +import { fetchAreaGeometries, fetchParkingAreaStats } from '../mobilityPlatformRequests/mobilityPlatformRequests'; import { isObjValid } from '../utils/utils'; import config from '../../../../config'; import { useAccessibleMap } from '../../../redux/selectors/settings'; @@ -44,15 +44,18 @@ const ParkingSpaces = () => { const isParkingStatisticsUrl = !parkingStatisticsUrl || parkingStatisticsUrl === 'undefined' ? null : parkingStatisticsUrl; useEffect(() => { + const controller = new AbortController(); + const { signal } = controller; if (showParkingSpaces && isParkingSpacesUrl && isParkingStatisticsUrl) { - fetchParkingAreaGeometries(isParkingSpacesUrl, setParkingSpaces, setFetchError); - fetchParkingAreaStats(isParkingStatisticsUrl, setParkingStatistics, setFetchError); + fetchAreaGeometries(isParkingSpacesUrl, setParkingSpaces, setFetchError, signal); + fetchParkingAreaStats(isParkingStatisticsUrl, setParkingStatistics, setFetchError, signal); } + return () => controller.abort(); }, [showParkingSpaces]); - const swapCoords = (inputData) => { + const swapCoords = inputData => { if (inputData.length > 0) { - return inputData.map((item) => item.map((v) => v.map((j) => [j[1], j[0]]))); + return inputData.map(item => item.map(v => v.map(j => [j[1], j[0]]))); } return inputData; }; @@ -64,7 +67,7 @@ const ParkingSpaces = () => { useEffect(() => { if (!fetchError && renderData) { const bounds = []; - parkingSpaces.forEach((item) => { + parkingSpaces.forEach(item => { bounds.push(swapCoords(item.geometry.coordinates)); }); map.fitBounds(bounds); @@ -72,7 +75,7 @@ const ParkingSpaces = () => { }, [showParkingSpaces, parkingSpaces, fetchError]); const renderColor = (itemId, capacity) => { - const stats = parkingStatistics?.find((item) => item.id === itemId); + const stats = parkingStatistics?.find(item => item.id === itemId); const almostFull = capacity * 0.85; const parkingCount = stats?.current_parking_count; if (parkingCount >= almostFull) { @@ -83,16 +86,16 @@ const ParkingSpaces = () => { return ( !fetchError && renderData - ? parkingSpaces.map((item) => ( + ? parkingSpaces.map(item => ( { + mouseover: e => { e.target.setStyle({ fillOpacity: useContrast ? '0.9' : '0.3' }); }, - mouseout: (e) => { + mouseout: e => { e.target.setStyle({ fillOpacity: useContrast ? '0.6' : '0.3' }); }, }} diff --git a/src/components/MobilityPlatform/ParkingSpaces/components/ParkingSpacesContent/ParkingSpacesContent.js b/src/components/MobilityPlatform/ParkingSpaces/components/ParkingSpacesContent/ParkingSpacesContent.js index 01bcf1762..14c7aa83b 100644 --- a/src/components/MobilityPlatform/ParkingSpaces/components/ParkingSpacesContent/ParkingSpacesContent.js +++ b/src/components/MobilityPlatform/ParkingSpaces/components/ParkingSpacesContent/ParkingSpacesContent.js @@ -1,37 +1,38 @@ import { Typography } from '@mui/material'; import PropTypes from 'prop-types'; import React from 'react'; +import { useIntl } from 'react-intl'; +import { StyledContainer, StyledHeaderContainer, StyledTextContainer } from '../../../styled/styled'; -const ParkingSpacesContent = ({ - classes, intl, parkingSpace, parkingStatistics, -}) => { - let freeParkingSpaces = 0; +const ParkingSpacesContent = ({ parkingSpace, parkingStatistics }) => { + const intl = useIntl(); + let freeParkingSpaces = 0; const parkingSpaceStats = parkingStatistics.filter(item => item.id === parkingSpace.id); - const renderText = (isTitle, translationId, text) => ( -
    - {isTitle ? ( - - {intl.formatMessage({ - id: translationId, - })} - - ) : ( - - {intl.formatMessage({ - id: translationId, - })} - : - {' '} - {text} - - )} -
    - ); + const renderText = (isTitle, translationId, text) => (isTitle ? ( + + + {intl.formatMessage({ + id: translationId, + })} + + + ) : ( + + + {intl.formatMessage({ + id: translationId, + })} + : + {' '} + {text} + + + )); const renderPaymentType = (translationId1, translationId2) => ( -
    + {intl.formatMessage({ id: translationId1, @@ -42,14 +43,14 @@ const ParkingSpacesContent = ({ id: translationId2, })} -
    + ); const renderParkingCount = (capacity, parkingCount) => { freeParkingSpaces = capacity - parkingCount; return ( -
    + {freeParkingSpaces > 0 ? ( {intl.formatMessage( @@ -62,27 +63,34 @@ const ParkingSpacesContent = ({ {intl.formatMessage({ id: 'mobilityPlatform.content.parkingSpaces.empty' })} )} -
    + ); }; return ( -
    + {renderText(true, 'mobilityPlatform.content.parkingSpaces.title')} {renderPaymentType('mobilityPlatform.content.parkingSpaces.type', 'mobilityPlatform.content.parkingSpaces.paid')} {renderText(false, 'mobilityPlatform.content.parkingSpaces.capacity', parkingSpace.properties.capacity_estimate)} {parkingSpaceStats && parkingSpaceStats.length > 0 && parkingSpaceStats.map(parking => renderParkingCount(parkingSpace.properties.capacity_estimate, parking.current_parking_count))} -
    + ); }; ParkingSpacesContent.propTypes = { - classes: PropTypes.objectOf(PropTypes.any).isRequired, - intl: PropTypes.objectOf(PropTypes.any).isRequired, - parkingSpace: PropTypes.objectOf(PropTypes.any), - parkingStatistics: PropTypes.arrayOf(PropTypes.any), + parkingSpace: PropTypes.shape({ + id: PropTypes.string, + properties: PropTypes.shape({ + capacity_estimate: PropTypes.number, + }), + }), + parkingStatistics: PropTypes.arrayOf( + PropTypes.shape({ + current_parking_count: PropTypes.number, + }), + ), }; ParkingSpacesContent.defaultProps = { diff --git a/src/components/MobilityPlatform/ParkingSpaces/components/ParkingSpacesContent/__tests__/__snapshots__/ParkingSpacesContent.test.js.snap b/src/components/MobilityPlatform/ParkingSpaces/components/ParkingSpacesContent/__tests__/__snapshots__/ParkingSpacesContent.test.js.snap index ac79a52d4..62ff2887d 100644 --- a/src/components/MobilityPlatform/ParkingSpaces/components/ParkingSpacesContent/__tests__/__snapshots__/ParkingSpacesContent.test.js.snap +++ b/src/components/MobilityPlatform/ParkingSpaces/components/ParkingSpacesContent/__tests__/__snapshots__/ParkingSpacesContent.test.js.snap @@ -3,10 +3,10 @@ exports[` should work 1`] = `

    should work 1`] = `

    should work 1`] = `

    should work 1`] = `

    ({ - container: { - margin: theme.spacing(1.25), - }, - title: { - width: '85%', - borderBottom: '1px solid #000000', - marginBottom: theme.spacing(1), - paddingBottom: theme.spacing(0.8), - }, - text: { - paddingBottom: theme.spacing(1), - }, -}); diff --git a/src/components/MobilityPlatform/PolygonComponent/PolygonComponent.js b/src/components/MobilityPlatform/PolygonComponent/PolygonComponent.js index f63d31f60..3d27c08a9 100644 --- a/src/components/MobilityPlatform/PolygonComponent/PolygonComponent.js +++ b/src/components/MobilityPlatform/PolygonComponent/PolygonComponent.js @@ -1,8 +1,9 @@ import React from 'react'; import { PropTypes } from 'prop-types'; +import { StyledPopupWrapper, StyledPopupInner } from '../styled/styled'; const PolygonComponent = ({ - classes, item, useContrast, pathOptions, children, + item, useContrast, pathOptions, children, }) => { const { Polygon, Popup } = global.rL; @@ -12,30 +13,33 @@ const PolygonComponent = ({ pathOptions={pathOptions} positions={item.geometry_coords} eventHandlers={{ - mouseover: (e) => { + mouseover: e => { e.target.setStyle({ fillOpacity: useContrast ? '0.6' : '0.2' }); }, - mouseout: (e) => { + mouseout: e => { e.target.setStyle({ fillOpacity: useContrast ? '0.3' : '0.2' }); }, }} > -

    + -
    - {children} -
    + {children}
    -
    + ); }; PolygonComponent.propTypes = { - classes: PropTypes.objectOf(PropTypes.any).isRequired, - item: PropTypes.objectOf(PropTypes.any).isRequired, + item: PropTypes.shape({ + id: PropTypes.string, + geometry_coords: PropTypes.arrayOf(PropTypes.arrayOf(PropTypes.arrayOf(PropTypes.number))), + }).isRequired, useContrast: PropTypes.bool, - pathOptions: PropTypes.objectOf(PropTypes.any).isRequired, + pathOptions: PropTypes.shape({ + color: PropTypes.string, + weight: PropTypes.number, + }).isRequired, children: PropTypes.node, }; diff --git a/src/components/MobilityPlatform/PolygonComponent/index.js b/src/components/MobilityPlatform/PolygonComponent/index.js index 3ebc90d80..37deaa711 100644 --- a/src/components/MobilityPlatform/PolygonComponent/index.js +++ b/src/components/MobilityPlatform/PolygonComponent/index.js @@ -1,5 +1,3 @@ -import { withStyles } from '@mui/styles'; import PolygonComponent from './PolygonComponent'; -import styles from './styles'; -export default withStyles(styles)(PolygonComponent); +export default PolygonComponent; diff --git a/src/components/MobilityPlatform/PolygonComponent/styles.js b/src/components/MobilityPlatform/PolygonComponent/styles.js deleted file mode 100644 index de9de2381..000000000 --- a/src/components/MobilityPlatform/PolygonComponent/styles.js +++ /dev/null @@ -1,17 +0,0 @@ -const styles = theme => ({ - popupWrapper: { - position: 'absolute', - textAlign: 'center', - marginBottom: theme.spacing(2), - width: '429px', - }, - popupInner: { - borderRadius: '3px', - marginBottom: theme.spacing(0.5), - marginLeft: theme.spacing(0.8), - lineHeight: 1.2, - overflowX: 'hidden', - }, -}); - -export default styles; diff --git a/src/components/MobilityPlatform/PublicBenches/PublicBenches.js b/src/components/MobilityPlatform/PublicBenches/PublicBenches.js index d753ead87..36abdc711 100644 --- a/src/components/MobilityPlatform/PublicBenches/PublicBenches.js +++ b/src/components/MobilityPlatform/PublicBenches/PublicBenches.js @@ -1,5 +1,5 @@ /* eslint-disable global-require */ -import React, { useState } from 'react'; +import React, { useEffect, useState } from 'react'; import PropTypes from 'prop-types'; import { useSelector } from 'react-redux'; import { useMap, useMapEvents } from 'react-leaflet'; @@ -58,18 +58,22 @@ const PublicBenches = ({ mapObject }) => { const fetchBox = MapUtility.getBboxFromBounds(wideBounds, true); const isDetailZoom = zoomLevel >= mapObject.options.detailZoom; + const controller = new AbortController(); const handlePublicBenches = () => { + const { signal } = controller; const options = { type_name: 'PublicBench', page_size: 1000, bbox: fetchBox, }; if ((showPublicBenches && isDetailZoom) || (embedded && isDetailZoom)) { - fetchMobilityMapData(options, setPublicBenchesData); + fetchMobilityMapData(options, setPublicBenchesData, signal); } }; + useEffect(() => () => controller.abort(), []); + const mapEvent = useMapEvents({ zoomend() { setZoomLevel(mapEvent.getZoom()); @@ -83,7 +87,7 @@ const PublicBenches = ({ mapObject }) => { const renderData = isDetailZoom && setRender(paramValue, embedded, showPublicBenches, publicBenchesData, isDataValid); return renderData - ? publicBenchesData.map((item) => ( + ? publicBenchesData.map(item => ( )) : null; diff --git a/src/components/MobilityPlatform/PublicToilets/components/PublicToiletsContent/PublicToiletsContent.js b/src/components/MobilityPlatform/PublicToilets/components/PublicToiletsContent/PublicToiletsContent.js index 7dd38ac1a..e53c8940a 100644 --- a/src/components/MobilityPlatform/PublicToilets/components/PublicToiletsContent/PublicToiletsContent.js +++ b/src/components/MobilityPlatform/PublicToilets/components/PublicToiletsContent/PublicToiletsContent.js @@ -1,48 +1,44 @@ import { Typography } from '@mui/material'; -import PropTypes from 'prop-types'; import React from 'react'; +import { useIntl } from 'react-intl'; +import { StyledContainer, StyledHeaderContainer, StyledTextContainer } from '../../../styled/styled'; -const PublicToiletsContent = ({ classes, intl }) => { - const titleTypo = (messageId, props = {}) => ( -
    +const PublicToiletsContent = () => { + const intl = useIntl(); + + const titleTypo = messageId => ( + {intl.formatMessage({ id: messageId, })} -
    + ); - const singleValTypo = (messageId, isSubtitle, props = {}) => ( -
    + const singleValTypo = (messageId, isSubtitle) => ( + {intl.formatMessage({ id: messageId, })} -
    + ); return ( -
    -
    {titleTypo('mobilityPlatform.content.publicToilets.title')}
    -
    + + {titleTypo('mobilityPlatform.content.publicToilets.title')} +
    {singleValTypo('mobilityPlatform.content.publicToilets.openNormalTitle', true)} {singleValTypo('mobilityPlatform.content.publicToilets.openNormalDate')} {singleValTypo('mobilityPlatform.content.publicToilets.openNormal')} - {singleValTypo('mobilityPlatform.content.publicToilets.openSummerTitle', true, { - className: classes.marginTop, - })} + {singleValTypo('mobilityPlatform.content.publicToilets.openSummerTitle', true)} {singleValTypo('mobilityPlatform.content.publicToilets.openSummerDate')} {singleValTypo('mobilityPlatform.content.publicToilets.openSummer')}
    -
    + ); }; -PublicToiletsContent.propTypes = { - classes: PropTypes.objectOf(PropTypes.any).isRequired, - intl: PropTypes.objectOf(PropTypes.any).isRequired, -}; - export default PublicToiletsContent; diff --git a/src/components/MobilityPlatform/PublicToilets/components/PublicToiletsContent/__tests__/__snapshots__/PublicToiletsContent.test.js.snap b/src/components/MobilityPlatform/PublicToilets/components/PublicToiletsContent/__tests__/__snapshots__/PublicToiletsContent.test.js.snap index a24bc22df..eefb894e2 100644 --- a/src/components/MobilityPlatform/PublicToilets/components/PublicToiletsContent/__tests__/__snapshots__/PublicToiletsContent.test.js.snap +++ b/src/components/MobilityPlatform/PublicToilets/components/PublicToiletsContent/__tests__/__snapshots__/PublicToiletsContent.test.js.snap @@ -3,12 +3,14 @@ exports[` should work 1`] = `
    -
    +

    @@ -16,24 +18,28 @@ exports[` should work 1`] = `

    -
    -
    +
    +

    Aukioloajat:

    -
    +

    Välillä: 1.10 - 30.4

    -
    +

    @@ -41,7 +47,7 @@ exports[` should work 1`] = `

    should work 1`] = ` Erityisaukioloajat:

    -
    +

    Välillä: 1.5 - 30.9

    -
    +

    diff --git a/src/components/MobilityPlatform/PublicToilets/components/PublicToiletsContent/index.js b/src/components/MobilityPlatform/PublicToilets/components/PublicToiletsContent/index.js index 3e8973508..c844c64bc 100644 --- a/src/components/MobilityPlatform/PublicToilets/components/PublicToiletsContent/index.js +++ b/src/components/MobilityPlatform/PublicToilets/components/PublicToiletsContent/index.js @@ -1,6 +1,3 @@ -import { withStyles } from '@mui/styles'; -import { injectIntl } from 'react-intl'; import PublicToiletsContent from './PublicToiletsContent'; -import styles from './styles'; -export default withStyles(styles)(injectIntl(PublicToiletsContent)); +export default PublicToiletsContent; diff --git a/src/components/MobilityPlatform/PublicToilets/components/PublicToiletsContent/styles.js b/src/components/MobilityPlatform/PublicToilets/components/PublicToiletsContent/styles.js deleted file mode 100644 index 9251213f1..000000000 --- a/src/components/MobilityPlatform/PublicToilets/components/PublicToiletsContent/styles.js +++ /dev/null @@ -1,20 +0,0 @@ -export default theme => ({ - container: { - margin: theme.spacing(1), - }, - headerContainer: { - width: '85%', - borderBottom: '1px solid #000', - paddingBottom: theme.spacing(0.5), - }, - textContainer: { - marginTop: theme.spacing(0.5), - }, - contentInner: { - marginLeft: theme.spacing(1), - marginBottom: theme.spacing(1), - }, - marginTop: { - marginTop: theme.spacing(0.5), - }, -}); diff --git a/src/components/MobilityPlatform/RailwayStations/RailwayStations.js b/src/components/MobilityPlatform/RailwayStations/RailwayStations.js index ba28978d7..0f79444eb 100644 --- a/src/components/MobilityPlatform/RailwayStations/RailwayStations.js +++ b/src/components/MobilityPlatform/RailwayStations/RailwayStations.js @@ -9,6 +9,7 @@ import { useAccessibleMap } from '../../../redux/selectors/settings'; import { fetchRailwaysData } from '../mobilityPlatformRequests/mobilityPlatformRequests'; import { createIcon, isDataValid } from '../utils/utils'; import RailwayStationsContent from './components/RailwayStationsContent'; +import { StyledPopupWrapper, StyledPopupInner } from '../styled/styled'; const RailwayStations = () => { const [railwayStations, setRailwayStations] = useState([]); @@ -25,9 +26,12 @@ const RailwayStations = () => { const customIcon = icon(createIcon(useContrast ? railwayIconBw : railwayIcon)); useEffect(() => { + const controller = new AbortController(); + const { signal } = controller; if (showRailwayStations && !railwayStations.length) { - fetchRailwaysData('metadata/stations', setRailwayStations); + fetchRailwaysData('metadata/stations', setRailwayStations, signal); } + return () => controller.abort(); }, [showRailwayStations]); /** Separate railway stations of Turku, eg. Turku station and Kupittaa */ @@ -49,9 +53,13 @@ const RailwayStations = () => { return renderData ? railwayStationsTku.map(item => ( - - - + + + + + + + )) : null; diff --git a/src/components/MobilityPlatform/RailwayStations/components/RailwayStationsContent/RailwayStationsContent.js b/src/components/MobilityPlatform/RailwayStations/components/RailwayStationsContent/RailwayStationsContent.js index c36a5c1e7..83b172219 100644 --- a/src/components/MobilityPlatform/RailwayStations/components/RailwayStationsContent/RailwayStationsContent.js +++ b/src/components/MobilityPlatform/RailwayStations/components/RailwayStationsContent/RailwayStationsContent.js @@ -3,11 +3,16 @@ import PropTypes from 'prop-types'; import { Typography } from '@mui/material'; import styled from '@emotion/styled'; import { format } from 'date-fns'; +import { useIntl } from 'react-intl'; import { fetchRailwaysData } from '../../../mobilityPlatformRequests/mobilityPlatformRequests'; +import { + StyledContainer, StyledHeaderContainer, StyledFlexContainer, StyledTextContainer, +} from '../../../styled/styled'; -const RailwayStationsContent = ({ intl, item, stationsData }) => { +const RailwayStationsContent = ({ item, stationsData }) => { const [stationTrainsData, setStationTrainsData] = useState([]); + const intl = useIntl(); const formatDateTime = timeValue => format(new Date(timeValue), 'HH:mm'); const optionsToParams = options => { @@ -19,6 +24,8 @@ const RailwayStationsContent = ({ intl, item, stationsData }) => { }; useEffect(() => { + const controller = new AbortController(); + const { signal } = controller; const options = { minutes_before_departure: 180, minutes_after_departure: 15, @@ -29,7 +36,8 @@ const RailwayStationsContent = ({ intl, item, stationsData }) => { const endpoint = `live-trains/station/${item.stationShortCode}`; const params = optionsToParams(options); const query = `${endpoint}?${params}`; - fetchRailwaysData(query, setStationTrainsData); + fetchRailwaysData(query, setStationTrainsData, signal); + return () => controller.abort(); }, [item.stationShortCode]); const filterArrivals = data => data.filter(train => { @@ -51,43 +59,51 @@ const RailwayStationsContent = ({ intl, item, stationsData }) => { const lastIdx = data.slice(-1)[0]; const arrivalStation = findStation(stationsData, lastIdx.stationShortCode); return ( - - {arrivalStation.stationName} - + + + {arrivalStation.stationName} + + ); }; const renderTrainInfo = train => ( - {`${train.trainType} ${train.trainNumber}`} + + {`${train.trainType} ${train.trainNumber}`} + ); const renderTimeValues = elem => ( - - {elem.liveEstimateTime && elem.differenceInMinutes > 1 - ? `${formatDateTime(elem.liveEstimateTime)} (${formatDateTime(elem.scheduledTime)})` - : `${formatDateTime(elem.scheduledTime)}`} - + + + {elem.liveEstimateTime && elem.differenceInMinutes > 1 + ? `${formatDateTime(elem.liveEstimateTime)} (${formatDateTime(elem.scheduledTime)})` + : `${formatDateTime(elem.scheduledTime)}`} + + ); return ( - - - + + + {item?.stationName} - - + +

    - + {intl.formatMessage({ id: 'mobilityPlatform.content.departingTrains.title' })} - - {!departingTrains?.length ? ( - - {intl.formatMessage({ id: 'mobilityPlatform.content.departingTrains.empty' })} - - ) : null} + + {!departingTrains?.length ? ( + + + {intl.formatMessage({ id: 'mobilityPlatform.content.departingTrains.empty' })} + + + ) : null} {departingTrains?.map(train => ( @@ -101,15 +117,17 @@ const RailwayStationsContent = ({ intl, item, stationsData }) => {
    - + {intl.formatMessage({ id: 'mobilityPlatform.content.arrivingTrains.title' })} - - {!arrivingTrains?.length ? ( - - {intl.formatMessage({ id: 'mobilityPlatform.content.arrivingTrains.empty' })} - - ) : null} + + {!arrivingTrains?.length ? ( + + + {intl.formatMessage({ id: 'mobilityPlatform.content.arrivingTrains.empty' })} + + + ) : null} {arrivingTrains?.map(train => ( @@ -122,59 +140,22 @@ const RailwayStationsContent = ({ intl, item, stationsData }) => { ))}
    - + ); }; const TrainIcon = styled.span(({ color }) => ({ fontSize: 20, - width: 20, - height: 20, + width: '20px', + height: '20px', lineHeight: '21px', - marginLeft: 6, - marginRight: 4, + marginLeft: '6px', + marginRight: '4px', + marginTop: '8px', color, })); -const StyledText = styled(Typography)(({ theme }) => ({ - marginBottom: theme.spacing(0.5), -})); - -const StyledTextContainer = styled.div(({ theme }) => ({ - marginBottom: theme.spacing(0.75), -})); - -const StyledFlexContainer = styled.div(({ theme }) => ({ - display: 'flex', - flexDirection: 'row', - justifyContent: 'space-between', - marginBottom: theme.spacing(0.75), - width: '90%', -})); - -const StyledPopupInner = styled.div(({ theme }) => ({ - borderRadius: '3px', - marginBottom: theme.spacing(1), - marginLeft: theme.spacing(1.2), - lineHeight: 1.2, - overflowX: 'hidden', -})); - -const StyledHeader = styled.div(({ theme }) => ({ - display: 'flex', - flexDirection: 'row', - marginTop: theme.spacing(0.5), - marginBottom: theme.spacing(1), - alignItems: 'flex-end', - borderBottom: '2px solid gray', - justifyContent: 'space-between', - width: '80%', -})); - RailwayStationsContent.propTypes = { - intl: PropTypes.shape({ - formatMessage: PropTypes.func, - }).isRequired, item: PropTypes.shape({ stationShortCode: PropTypes.string, stationName: PropTypes.string, diff --git a/src/components/MobilityPlatform/RailwayStations/components/RailwayStationsContent/__tests__/__snapshots__/RailwayStationsContent.test.js.snap b/src/components/MobilityPlatform/RailwayStations/components/RailwayStationsContent/__tests__/__snapshots__/RailwayStationsContent.test.js.snap index 07eb20b81..073e3349b 100644 --- a/src/components/MobilityPlatform/RailwayStations/components/RailwayStationsContent/__tests__/__snapshots__/RailwayStationsContent.test.js.snap +++ b/src/components/MobilityPlatform/RailwayStations/components/RailwayStationsContent/__tests__/__snapshots__/RailwayStationsContent.test.js.snap @@ -3,13 +3,13 @@ exports[` should match snapshot 1`] = `

    Kupittaa

    @@ -17,15 +17,19 @@ exports[` should match snapshot 1`] = `
    - Lähtevät junat + Lähtevät junat:
    +
    +

    Ei lähteviä junia

    @@ -33,15 +37,19 @@ exports[` should match snapshot 1`] = `
    - Saapuvat junat + Saapuvat junat:
    +
    +

    Ei saapuvia junia

    diff --git a/src/components/MobilityPlatform/RailwayStations/components/RailwayStationsContent/index.js b/src/components/MobilityPlatform/RailwayStations/components/RailwayStationsContent/index.js index 40c76cd7f..4c40dd131 100644 --- a/src/components/MobilityPlatform/RailwayStations/components/RailwayStationsContent/index.js +++ b/src/components/MobilityPlatform/RailwayStations/components/RailwayStationsContent/index.js @@ -1,4 +1,3 @@ -import { injectIntl } from 'react-intl'; import RailwayStationsContent from './RailwayStationsContent'; -export default injectIntl(RailwayStationsContent); +export default RailwayStationsContent; diff --git a/src/components/MobilityPlatform/RentalCars/RentalCars.js b/src/components/MobilityPlatform/RentalCars/RentalCars.js index 1618b5ebe..378d5ae0c 100644 --- a/src/components/MobilityPlatform/RentalCars/RentalCars.js +++ b/src/components/MobilityPlatform/RentalCars/RentalCars.js @@ -1,5 +1,4 @@ /* eslint-disable react-hooks/exhaustive-deps */ -import { PropTypes } from 'prop-types'; import React, { useEffect, useState } from 'react'; import { useMap, useMapEvents } from 'react-leaflet'; import { useSelector } from 'react-redux'; @@ -8,13 +7,13 @@ import providerIcon from 'servicemap-ui-turku/assets/icons/icons-icon_24rent.svg import rentalCarIcon from 'servicemap-ui-turku/assets/icons/icons-icon_rental_car.svg'; import { useMobilityPlatformContext } from '../../../context/MobilityPlatformContext'; import { useAccessibleMap } from '../../../redux/selectors/settings'; -import { fetchIotData } from '../mobilityPlatformRequests/mobilityPlatformRequests'; +import useIotDataFetch from '../utils/useIotDataFetch'; import { isDataValid, setRender, checkMapType } from '../utils/utils'; import { isEmbed } from '../../../utils/path'; +import { StyledPopupWrapper, StyledPopupInner } from '../styled/styled'; import RentalCarsContent from './components/RentalCarsContent'; -const RentalCars = ({ classes }) => { - const [rentalCarsData, setRentalCarsData] = useState([]); +const RentalCars = () => { const [zoomLevel, setZoomLevel] = useState(13); const { showRentalCars } = useMobilityPlatformContext(); @@ -40,11 +39,7 @@ const RentalCars = ({ classes }) => { iconSize: zoomLevel < 14 ? [45, 45] : [50, 56], }); - useEffect(() => { - if (showRentalCars || embedded) { - fetchIotData('R24', setRentalCarsData); - } - }, [showRentalCars, embedded]); + const { iotData: rentalCarsData } = useIotDataFetch('R24', showRentalCars, embedded); const map = useMap(); @@ -54,7 +49,7 @@ const RentalCars = ({ classes }) => { useEffect(() => { if (renderData && !embedded) { const bounds = []; - rentalCarsData.forEach((item) => { + rentalCarsData.forEach(item => { bounds.push([item.homeLocationData.coordinates.latitude, item.homeLocationData.coordinates.longitude]); }); map.fitBounds(bounds); @@ -62,32 +57,26 @@ const RentalCars = ({ classes }) => { }, [showRentalCars, rentalCarsData]); return ( - <> - {renderData ? ( - rentalCarsData.filter(item => item.availabilityData.available).map(item => ( - -
    - -
    - -
    -
    -
    -
    - )) - ) : null} - + renderData ? ( + rentalCarsData.filter(item => item.availabilityData.available).map(item => ( + + + + + + + + + + )) + ) : null ); }; -RentalCars.propTypes = { - classes: PropTypes.objectOf(PropTypes.any).isRequired, -}; - export default RentalCars; diff --git a/src/components/MobilityPlatform/RentalCars/components/RentalCarsContent/RentalCarsContent.js b/src/components/MobilityPlatform/RentalCars/components/RentalCarsContent/RentalCarsContent.js index 2fb495dad..780a64bc8 100644 --- a/src/components/MobilityPlatform/RentalCars/components/RentalCarsContent/RentalCarsContent.js +++ b/src/components/MobilityPlatform/RentalCars/components/RentalCarsContent/RentalCarsContent.js @@ -2,30 +2,36 @@ import { Link, Typography } from '@mui/material'; import PropTypes from 'prop-types'; import React from 'react'; import { useSelector } from 'react-redux'; +import { useIntl } from 'react-intl'; +import styled from '@emotion/styled'; +import { + StyledContainer, StyledHeaderContainer, StyledTextContainer, StyledLinkText, +} from '../../../styled/styled'; -const RentalCarsContent = ({ classes, intl, car }) => { +const RentalCarsContent = ({ car }) => { + const intl = useIntl(); const locale = useSelector(state => state.user.locale); - const titleText = (messageId, props = {}) => ( -
    - + const titleText = messageId => ( + + {intl.formatMessage({ id: messageId, })} -
    + ); const contentText = (messageId, value) => ( -
    + {intl.formatMessage({ id: messageId }, { value })} -
    + ); const renderCarInfo = (messageId, manufacturer, model) => ( -
    + {intl.formatMessage({ id: messageId, @@ -36,12 +42,12 @@ const RentalCarsContent = ({ classes, intl, car }) => { {' '} {model} -
    + ); const serviceProvider = '24Rent'; - const getLink = (address) => { + const getLink = address => { if (locale === 'en') { return `https://www.24rent.fi/en/#/?city=${address}`; } @@ -49,46 +55,59 @@ const RentalCarsContent = ({ classes, intl, car }) => { }; return ( -
    + {titleText('mobilityPlatform.content.rentalCars.title')} {contentText('mobilityPlatform.content.general.provider', serviceProvider)} -
    + - + {intl.formatMessage({ id: 'mobilityPlatform.content.rentalCars.link', })} - + -
    + {renderCarInfo( 'mobilityPlatform.content.rentalCars.carInfo', car.vehicleModelData.manufacturer, car.vehicleModelData.name, )} -
    + {car.availabilityData.available ? intl.formatMessage({ id: 'mobilityPlatform.content.rentalCars.available' }) : intl.formatMessage({ id: 'mobilityPlatform.content.rentalCars.reserved' })} -
    + {contentText('mobilityPlatform.content.rentalCars.address', car.homeLocationData.fullAddress)}
    shared use car
    -
    + ); }; +const StyledLinkContainer = styled.div(({ theme }) => ({ + marginTop: theme.spacing(0.4), + width: '55%', +})); + RentalCarsContent.propTypes = { - classes: PropTypes.objectOf(PropTypes.any).isRequired, - intl: PropTypes.objectOf(PropTypes.any).isRequired, - car: PropTypes.objectOf(PropTypes.any), + car: PropTypes.shape({ + id: PropTypes.string, + homeLocationData: PropTypes.shape({ + fullAddress: PropTypes.string, + }), + vehicleModelData: PropTypes.shape({ + manufacturer: PropTypes.string, + name: PropTypes.string, + }), + availabilityData: PropTypes.objectOf(PropTypes.bool), + }), }; RentalCarsContent.defaultProps = { - car: null, + car: {}, }; export default RentalCarsContent; diff --git a/src/components/MobilityPlatform/RentalCars/components/RentalCarsContent/__tests__/__snapshots__/RentalCarsContent.test.js.snap b/src/components/MobilityPlatform/RentalCars/components/RentalCarsContent/__tests__/__snapshots__/RentalCarsContent.test.js.snap index 08e139af6..edb9b367d 100644 --- a/src/components/MobilityPlatform/RentalCars/components/RentalCarsContent/__tests__/__snapshots__/RentalCarsContent.test.js.snap +++ b/src/components/MobilityPlatform/RentalCars/components/RentalCarsContent/__tests__/__snapshots__/RentalCarsContent.test.js.snap @@ -3,10 +3,10 @@ exports[` should work 1`] = `

    should work 1`] = `

    should work 1`] = `

    should work 1`] = `

    should work 1`] = `

    ({ - container: { - margin: theme.spacing(1), - }, - title: { - width: '94%', - borderBottom: '1px solid #000000', - marginBottom: theme.spacing(1), - }, - text: { - paddingBottom: theme.spacing(1), - }, - linkContainer: { - paddingBottom: theme.spacing(1), - width: '55%', - }, - link: { - color: theme.palette.link.main, - textDecoration: 'underline', - }, -}); diff --git a/src/components/MobilityPlatform/RentalCars/index.js b/src/components/MobilityPlatform/RentalCars/index.js index 750f28c9b..45d73d1b0 100644 --- a/src/components/MobilityPlatform/RentalCars/index.js +++ b/src/components/MobilityPlatform/RentalCars/index.js @@ -1,6 +1,3 @@ -import { withStyles } from '@mui/styles'; -import { injectIntl } from 'react-intl'; import RentalCars from './RentalCars'; -import styles from './styles'; -export default withStyles(styles)(injectIntl(RentalCars)); +export default RentalCars; diff --git a/src/components/MobilityPlatform/RentalCars/styles.js b/src/components/MobilityPlatform/RentalCars/styles.js deleted file mode 100644 index f89a68ef2..000000000 --- a/src/components/MobilityPlatform/RentalCars/styles.js +++ /dev/null @@ -1,5 +0,0 @@ -export default theme => ({ - popupInner: { - padding: theme.spacing(1.5), - }, -}); diff --git a/src/components/MobilityPlatform/Roadworks/Roadworks.js b/src/components/MobilityPlatform/Roadworks/Roadworks.js index e54355e02..e1b622d60 100644 --- a/src/components/MobilityPlatform/Roadworks/Roadworks.js +++ b/src/components/MobilityPlatform/Roadworks/Roadworks.js @@ -1,21 +1,28 @@ /* eslint-disable react-hooks/exhaustive-deps */ -import React, { useEffect, useState } from 'react'; +import React, { useEffect } from 'react'; import { useSelector } from 'react-redux'; import { useMap } from 'react-leaflet'; import roadworksIcon from 'servicemap-ui-turku/assets/icons/icons-icon_roadworks.svg'; import roadworksIconBw from 'servicemap-ui-turku/assets/icons/contrast/icons-icon_roadworks-bw.svg'; import { useMobilityPlatformContext } from '../../../context/MobilityPlatformContext'; -import { fetchParkingAreaGeometries } from '../mobilityPlatformRequests/mobilityPlatformRequests'; import { createIcon, isDataValid, grayOptionsBase, whiteOptionsBase, } from '../utils/utils'; import { useAccessibleMap, getCitySettings } from '../../../redux/selectors/settings'; import config from '../../../../config'; +import useRoadworksDataFetch from '../utils/useRoadworksDataFetch'; +import { StyledPopupWrapper, StyledPopupInner } from '../styled/styled'; import RoadworksContent from './components/RoadworksContent'; const Roadworks = () => { - const [roadworksData, setRoadworksData] = useState([]); - const [trafficAnnouncementsData, setTrafficAnnouncementsData] = useState([]); + const getOptions = typeStr => { + const options = { + page_size: 200, + is_active: true, + situation_type_str: typeStr, + }; + return options; + }; const { showRoadworks } = useMobilityPlatformContext(); @@ -27,31 +34,16 @@ const Roadworks = () => { const useContrast = useSelector(useAccessibleMap); const citySettings = useSelector(getCitySettings); - const roadworksUrl = config.roadworksAPI; - const isRoadworksUrl = !roadworksUrl || roadworksUrl === 'undefined' ? null : roadworksUrl; - const customIcon = icon(createIcon(useContrast ? roadworksIconBw : roadworksIcon)); const grayOptions = grayOptionsBase({ dashArray: '2, 5, 8' }); const whiteOptions = whiteOptionsBase({ dashArray: !useContrast ? '1, 8' : null }); - useEffect(() => { - const endpoint = `${isRoadworksUrl}?inactiveHours=0&includeAreaGeometry=true&situationType=ROAD_WORK`; - if (showRoadworks && isRoadworksUrl) { - fetchParkingAreaGeometries(endpoint, setRoadworksData); - } - }, [showRoadworks]); - - useEffect(() => { - const endpoint = `${isRoadworksUrl}?inactiveHours=0&includeAreaGeometry=true&situationType=TRAFFIC_ANNOUNCEMENT`; - if (showRoadworks && isRoadworksUrl) { - fetchParkingAreaGeometries(endpoint, setTrafficAnnouncementsData); - } - }, [showRoadworks]); - + const { data: roadworksData } = useRoadworksDataFetch(getOptions('ROAD_WORK'), showRoadworks); + const { data: trafficAnnouncementsData } = useRoadworksDataFetch(getOptions('TRAFFIC_ANNOUNCEMENT'), showRoadworks); const roadworksDataFull = [].concat(roadworksData, trafficAnnouncementsData); - const checkCitySettings = (citiesArray) => { + const checkCitySettings = citiesArray => { if (citiesArray?.length > 0) { return citiesArray; } @@ -60,63 +52,98 @@ const Roadworks = () => { /** Separate roadworks of Turku from the rest */ const roadworksFiltered = roadworksDataFull.reduce((acc, curr) => { - const roadWorkDetails = curr?.properties?.announcements[0]; - const selectedCities = config.cities.filter((c) => citySettings[c]); + const roadWorkDetails = curr?.announcements[0]; + const selectedCities = config.cities.filter(c => citySettings[c]); const cities = checkCitySettings(selectedCities); if ( - cities.includes(roadWorkDetails?.locationDetails?.roadAddressLocation?.primaryPoint?.municipality.toLowerCase()) + cities.includes(roadWorkDetails?.location?.details?.primaryPoint?.municipality.toLowerCase()) ) { acc.push(curr); } return acc; }, []); - /** Separate roadworks that contain Point type geometry from the rest */ - const roadworksPoints = roadworksFiltered.reduce((acc, curr) => { - if (curr.geometry.type === 'Point') { + /** + * Separate roadworks from the rest by geometry type (eg. POINT or LINESTRING) + * @param {array} data + * @param {string} geomType + * @returns array + */ + const filterRoadworksByGeometry = (data, geomType) => data.reduce((acc, curr) => { + if (curr?.announcements[0]?.location?.geometry?.includes(geomType)) { acc.push(curr); } return acc; }, []); - /** Separate roadworks that contain LineString type geometry from the rest */ - const roadworksLines = roadworksFiltered.reduce((acc, curr) => { - if (curr.geometry.type === 'LineString') { - acc.push(curr); - } - return acc; - }, []); + const roadworksPoints = filterRoadworksByGeometry(roadworksFiltered, 'POINT'); + const roadworksLines = filterRoadworksByGeometry(roadworksFiltered, ';LINESTRING'); + const roadworksMultiLines = filterRoadworksByGeometry(roadworksFiltered, 'MULTILINESTRING'); - /** Separate roadworks that contain MultiLineString type geometry from the rest */ - const roadworksMultiLines = roadworksFiltered.reduce((acc, curr) => { - if (curr.geometry.type === 'MultiLineString') { - acc.push(curr); + /** + * Gets coordinates from string, for example 'SRID=4326;POINT (22.37835 60.40831)'. + * Use regex to get numerical values and place those inside an array + * @param {string} inputString + * @returns {*array} coordinates + */ + const getPointCoordinates = inputString => { + const regex = /POINT \((\d+\.\d+) (\d+\.\d+)\)/; + const match = inputString.match(regex); + if (match) { + const coordinates = [parseFloat(match[2]), parseFloat(match[1])]; + return coordinates; } - return acc; - }, []); + return []; + }; /** - * Swap coordinates of linestrings - * @param {array} inputData + * Get coordinates from string that includes geometry in linestring format. + * Remove letters and special characters and return nested array from numbers. + * @param {string} lineString * @returns array */ - const swapCoords = (inputData) => { - if (inputData?.length > 0) { - return inputData.map((coordinates) => [coordinates[1], coordinates[0]]); - } - return inputData; + const getLineCoordinates = lineString => { + const coordinatesString = lineString.replace(/^SRID=\d+;LINESTRING \((.*)\)$/, '$1'); + const coordinatePairs = coordinatesString.split(', ').map(pair => pair.split(' ')); + const coordinates = coordinatePairs.map(pair => [parseFloat(pair[1]), parseFloat(pair[0])]); + return coordinates; }; /** - * Swap coordinates of multi linestring - * @param {array} inputData + * Get coordinates from string that includes geometry in multilinestring format. + * Remove letters and special characters and return nested array from numbers. + * @param {string} inputString * @returns array */ - const swapCoordsMulti = (inputData) => { - if (inputData?.length > 0) { - return inputData.map((innerArray) => innerArray.map((coordinates) => [coordinates[1], coordinates[0]])); - } - return inputData; + const getMultiLineCoordinates = inputString => { + const multiLineStrings = inputString.replace(/^SRID=\d+;MULTILINESTRING \((.*)\)$/, '$1').split('), '); + const nestedCoordinates = multiLineStrings.map(lineString => { + const cleanedLineString = lineString.replace(/^\(/, '').replace(/\)$/, ''); + const coordinatePairs = cleanedLineString.split(', ').map(pair => pair.split(' ')); + return coordinatePairs.map(pair => [parseFloat(pair[1]), parseFloat(pair[0])]); + }); + return nestedCoordinates; + }; + + /** + * Get single pair of coordinates from nested arrays (2 or 3 levels). + * @param {array} data + * @param {boolean} isMulti + * @returns array + */ + const getSingleCoordinates = (data, isMulti) => { + const coords = isMulti ? data[0][0] : data[0]; + return [coords[0], coords[1]]; + }; + + const parseMultiAndGetSingleCoordinates = multilineStr => { + const coordinates = getMultiLineCoordinates(multilineStr); + return getSingleCoordinates(coordinates, true); + }; + + const parseLineAndGetSingleCoordinates = lineStr => { + const coordinates = getLineCoordinates(lineStr); + return getSingleCoordinates(coordinates, false); }; const areMarkersValid = isDataValid(showRoadworks, roadworksPoints); @@ -126,58 +153,64 @@ const Roadworks = () => { useEffect(() => { if (areMultiLinesValid) { const bounds = []; - roadworksMultiLines.forEach((item) => { - bounds.push(swapCoordsMulti(item.geometry.coordinates)); + roadworksMultiLines.forEach(item => { + bounds.push(getMultiLineCoordinates(item?.announcements[0]?.location?.geometry)); }); map.fitBounds(bounds); } }, [showRoadworks, roadworksMultiLines]); - const renderContent = (item) => ( - - - + const renderContent = item => ( + + + + + + + ); - const getSingleCoordinates = (data) => { - const coords = data[0][0]; - return [coords[1], coords[0]]; - }; - const renderMarkers = () => (areMarkersValid - ? roadworksPoints.map((item) => ( + ? roadworksPoints.map(item => ( {renderContent(item)} )) : null); - const renderLines = () => (areLinesValid ? roadworksLines.map((item) => ( - - {renderContent(item)} - - )) : null); + const renderLines = () => (areLinesValid ? ( + roadworksLines.map(item => ( + + + + {renderContent(item)} + + + )) + ) : null); const renderMultiLines = () => (areMultiLinesValid ? ( - roadworksMultiLines.map((item) => ( - + roadworksMultiLines.map(item => ( + {renderContent(item)} diff --git a/src/components/MobilityPlatform/Roadworks/components/RoadworksContent/RoadworksContent.js b/src/components/MobilityPlatform/Roadworks/components/RoadworksContent/RoadworksContent.js index 66505e5ee..5e5877fca 100644 --- a/src/components/MobilityPlatform/Roadworks/components/RoadworksContent/RoadworksContent.js +++ b/src/components/MobilityPlatform/Roadworks/components/RoadworksContent/RoadworksContent.js @@ -3,25 +3,26 @@ import React from 'react'; import PropTypes from 'prop-types'; import styled from '@emotion/styled'; import { format } from 'date-fns'; +import { StyledContainer, StyledHeaderContainer, StyledTextContainer } from '../../../styled/styled'; const RoadworksContent = ({ item }) => { - const roadworkDetails = item?.properties?.announcements[0]; + const roadworkDetails = item?.announcements[0]; - const formatDate = (dateTimeValue) => format(new Date(dateTimeValue), 'dd.MM.yyyy'); + const formatDate = dateTimeValue => format(new Date(dateTimeValue), 'dd.MM.yyyy'); - const filterRestrictions = (restrictionsData) => { + const filterRestrictions = restrictionsData => { const restrictionTypes = ['SPEED_LIMIT', 'SPEED_LIMIT_LENGTH']; if (restrictionsData?.length > 0) { - return restrictionsData.filter((restriction) => restrictionTypes.includes(restriction.type)); + return restrictionsData.filter(restriction => restrictionTypes.includes(restriction.type)); } return []; }; - const roadWorksRestrictions = filterRestrictions(roadworkDetails.roadWorkPhases[0]?.restrictions); + const roadWorksRestrictions = filterRestrictions(roadworkDetails.additional_info?.restrictions); const renderRestrictions = () => ( - roadWorksRestrictions?.length > 0 ? ( - roadWorksRestrictions.map((limitItem) => ( + roadWorksRestrictions?.length ? ( + roadWorksRestrictions.map(limitItem => ( {`${limitItem.restriction.name}: ${limitItem.restriction.quantity} ${limitItem.restriction.unit}`} @@ -32,8 +33,8 @@ const RoadworksContent = ({ item }) => { ); const renderExtraFeatures = () => ( - roadworkDetails.features?.length > 0 ? ( - roadworkDetails.features.map((feature) => ( + roadworkDetails.features?.length ? ( + roadworkDetails.features.map(feature => ( {feature.quantity && feature.unit ? ( @@ -54,22 +55,22 @@ const RoadworksContent = ({ item }) => { ); const renderDateValues = () => { - if (roadworkDetails?.timeAndDuration.startTime && roadworkDetails?.timeAndDuration.endTime) { + if (roadworkDetails?.additional_info?.timeAndDuration?.startTime && roadworkDetails?.additional_info.timeAndDuration.endTime) { return ( - {`Aika: ${formatDate(roadworkDetails?.timeAndDuration.startTime)} - ${formatDate( - roadworkDetails?.timeAndDuration.endTime, + {`Aika: ${formatDate(roadworkDetails?.additional_info?.timeAndDuration?.startTime)} - ${formatDate( + roadworkDetails?.additional_info?.timeAndDuration?.endTime, )}`} ); } - if (roadworkDetails.timeAndDuration.startTime) { + if (roadworkDetails?.additional_info?.timeAndDuration?.startTime) { return ( - {`Työ alkoi: ${formatDate(roadworkDetails?.timeAndDuration.startTime)}`} + {`Työ alkoi: ${formatDate(roadworkDetails?.additional_info?.timeAndDuration?.startTime)}`} ); @@ -78,15 +79,15 @@ const RoadworksContent = ({ item }) => { }; return ( - - + + {roadworkDetails?.title} - +

    - {roadworkDetails?.location?.description} + {roadworkDetails?.description} {roadworkDetails.comment ? ( @@ -97,7 +98,7 @@ const RoadworksContent = ({ item }) => { {renderExtraFeatures()} {renderDateValues()}
    - + ); }; @@ -105,46 +106,22 @@ const StyledText = styled(Typography)(({ theme }) => ({ marginBottom: theme.spacing(0.5), })); -const StyledTextContainer = styled.div(({ theme }) => ({ - marginBottom: theme.spacing(0.75), -})); - -const StyledPopupInner = styled.div(({ theme }) => ({ - borderRadius: '3px', - marginBottom: theme.spacing(1), - marginLeft: theme.spacing(1.2), - lineHeight: 1.2, - overflowX: 'hidden', -})); - -const StyledHeader = styled.div(({ theme }) => ({ - display: 'flex', - flexDirection: 'row', - marginTop: theme.spacing(0.5), - marginBottom: theme.spacing(1), - alignItems: 'flex-end', - borderBottom: '2px solid gray', - justifyContent: 'space-between', - width: '86%', -})); - RoadworksContent.propTypes = { item: PropTypes.shape({ - properties: PropTypes.shape({ - situationType: PropTypes.string, - announcements: PropTypes.arrayOf( - PropTypes.shape({ - title: PropTypes.string, - location: PropTypes.shape({ - description: PropTypes.string, - }), + situationType: PropTypes.string, + announcements: PropTypes.arrayOf( + PropTypes.shape({ + title: PropTypes.string, + description: PropTypes.string, + comment: PropTypes.string, + additional_info: PropTypes.shape({ timeAndDuration: PropTypes.shape({ startTime: PropTypes.string, endTime: PropTypes.string, }), }), - ), - }), + }), + ), }), }; diff --git a/src/components/MobilityPlatform/Roadworks/components/RoadworksContent/__tests__/RoadworksContent.test.js b/src/components/MobilityPlatform/Roadworks/components/RoadworksContent/__tests__/RoadworksContent.test.js index f54e8da31..68bf67141 100644 --- a/src/components/MobilityPlatform/Roadworks/components/RoadworksContent/__tests__/RoadworksContent.test.js +++ b/src/components/MobilityPlatform/Roadworks/components/RoadworksContent/__tests__/RoadworksContent.test.js @@ -7,35 +7,29 @@ import RoadworksContent from '../index'; const mockProps = { item: { - properties: { - announcements: [ - { - title: 'Test roadwork', - location: { - description: 'Test desc', - }, - comment: 'Comment', - timeAndDuration: { - startTime: '2023-11-05T10:00:00z', - endTime: '2023-12-05T17:00:00z', - }, - roadWorkPhases: [ + announcements: [ + { + title: 'Test roadwork', + description: 'Test desc', + comment: 'Comment', + additional_info: { + restrictions: [ { - restrictions: [ - { - type: 'SPEED_LIMIT', - restriction: { - name: 'Test limit', - quantity: '50', - unit: 'km/h', - }, - }, - ], + type: 'SPEED_LIMIT', + restriction: { + name: 'Test limit', + quantity: '50', + unit: 'km/h', + }, }, ], + timeAndDuration: { + startTime: '2024-04-10T10:00:00z', + endTime: '2024-06-05T17:00:00z', + }, }, - ], - }, + }, + ], }, }; @@ -54,14 +48,14 @@ describe('', () => { const h4 = container.querySelectorAll('h4'); const p = container.querySelectorAll('p'); - const roadworkDetails = mockProps.item.properties.announcements[0]; - const restrictionsArray = roadworkDetails.roadWorkPhases[0].restrictions[0]; + const roadworkDetails = mockProps.item.announcements[0]; + const restrictionsArray = roadworkDetails.additional_info.restrictions[0]; expect(h4[0].textContent).toContain(roadworkDetails.title); - expect(p[0].textContent).toContain(roadworkDetails.location.description); + expect(p[0].textContent).toContain(roadworkDetails.description); expect(p[1].textContent).toContain(roadworkDetails.comment); expect(p[2].textContent).toContain( `${restrictionsArray.restriction.name}: ${restrictionsArray.restriction.quantity} ${restrictionsArray.restriction.unit}`, ); - expect(p[3].textContent).toEqual('Aika: 05.11.2023 - 05.12.2023'); + expect(p[3].textContent).toEqual('Aika: 10.04.2024 - 05.06.2024'); }); }); diff --git a/src/components/MobilityPlatform/Roadworks/components/RoadworksContent/__tests__/__snapshots__/RoadworksContent.test.js.snap b/src/components/MobilityPlatform/Roadworks/components/RoadworksContent/__tests__/__snapshots__/RoadworksContent.test.js.snap index b485c9b72..32468223b 100644 --- a/src/components/MobilityPlatform/Roadworks/components/RoadworksContent/__tests__/__snapshots__/RoadworksContent.test.js.snap +++ b/src/components/MobilityPlatform/Roadworks/components/RoadworksContent/__tests__/__snapshots__/RoadworksContent.test.js.snap @@ -3,10 +3,10 @@ exports[` does match snapshot 1`] = `

    does match snapshot 1`] = `

    does match snapshot 1`] = `

    does match snapshot 1`] = `

    does match snapshot 1`] = `

    - Aika: 05.11.2023 - 05.12.2023 + Aika: 10.04.2024 - 05.06.2024

    diff --git a/src/components/MobilityPlatform/Scooters/components/ScooterMarkers/ScooterMarkers.js b/src/components/MobilityPlatform/Scooters/components/ScooterMarkers/ScooterMarkers.js index d459729e1..7a09e65d8 100644 --- a/src/components/MobilityPlatform/Scooters/components/ScooterMarkers/ScooterMarkers.js +++ b/src/components/MobilityPlatform/Scooters/components/ScooterMarkers/ScooterMarkers.js @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from 'react'; +import React, { useState } from 'react'; import PropTypes from 'prop-types'; import { useSelector } from 'react-redux'; import { useMap, useMapEvents } from 'react-leaflet'; @@ -6,13 +6,12 @@ import rydeIcon from 'servicemap-ui-turku/assets/icons/icons-icon_ryde.svg'; import rydeIconBw from 'servicemap-ui-turku/assets/icons/contrast/icons-icon_ryde-bw.svg'; import { useMobilityPlatformContext } from '../../../../../context/MobilityPlatformContext'; import { useAccessibleMap } from '../../../../../redux/selectors/settings'; -import { fetchIotData } from '../../../mobilityPlatformRequests/mobilityPlatformRequests'; +import useIotDataFetch from '../../../utils/useIotDataFetch'; import ScooterInfo from './components/ScooterInfo'; import { isDataValid } from '../../../utils/utils'; +import { StyledPopupWrapper, StyledPopupInner } from '../../../styled/styled'; const ScooterMarkers = ({ mapObject }) => { - const [scooterData, setScooterData] = useState([]); - const { showScootersRyde } = useMobilityPlatformContext(); const useContrast = useSelector(useAccessibleMap); @@ -30,7 +29,6 @@ const ScooterMarkers = ({ mapObject }) => { }); const isDetailZoom = zoomLevel >= mapObject.options.detailZoom; - const setProviderIcon = useContrast ? rydeIconBw : rydeIcon; const customIcon = icon({ @@ -38,44 +36,45 @@ const ScooterMarkers = ({ mapObject }) => { iconSize: [40, 40], }); - useEffect(() => { - if (showScootersRyde) { - fetchIotData('SDR', setScooterData, true); - } - }, [showScootersRyde]); + const { iotData: scooterData } = useIotDataFetch('SDR', showScootersRyde); - const filterByBounds = (data) => { - if (data && data.length > 0) { - return data.filter(item => map.getBounds().contains([item.lat, item.lon])); + const filterByBounds = data => { + if (data?.length) { + return data?.filter(item => map.getBounds().contains([item.lat, item.lon])); } return []; }; const filteredScooters = filterByBounds(scooterData); - const renderData = isDataValid(showScootersRyde, filteredScooters) && isDetailZoom; return ( - <> - {renderData ? ( - filteredScooters.map(item => ( - + renderData ? ( + filteredScooters.map(item => ( + + - + + + - - )) - ) : null} - + + + )) + ) : null ); }; ScooterMarkers.propTypes = { - mapObject: PropTypes.objectOf(PropTypes.any).isRequired, + mapObject: PropTypes.shape({ + options: PropTypes.shape({ + detailZoom: PropTypes.number, + }), + }).isRequired, }; export default ScooterMarkers; diff --git a/src/components/MobilityPlatform/Scooters/components/ScooterMarkers/components/ScooterInfo/ScooterInfo.js b/src/components/MobilityPlatform/Scooters/components/ScooterMarkers/components/ScooterInfo/ScooterInfo.js index 89fab0bd0..af86b17a6 100644 --- a/src/components/MobilityPlatform/Scooters/components/ScooterMarkers/components/ScooterInfo/ScooterInfo.js +++ b/src/components/MobilityPlatform/Scooters/components/ScooterMarkers/components/ScooterInfo/ScooterInfo.js @@ -1,79 +1,85 @@ import { Link, Typography } from '@mui/material'; import PropTypes from 'prop-types'; import React from 'react'; +import { useIntl } from 'react-intl'; +import { + StyledContainer, StyledHeaderContainer, StyledTextContainer, StyledBoldText, StyledLinkText, +} from '../../../../../styled/styled'; + +const ScooterInfo = ({ item }) => { + const intl = useIntl(); -const ScooterInfo = ({ classes, intl, item }) => { const titleTypo = messageId => ( -
    - - {intl.formatMessage({ - id: messageId, - })} - -
    + + {intl.formatMessage({ + id: messageId, + })} + ); const singleValTypo = (messageId, value) => ( -
    + {intl.formatMessage({ id: messageId }, { value })} -
    + ); - const renderStatus = (scooterStatus) => { + const renderStatus = scooterStatus => { if (!scooterStatus) { return ( -
    + {intl.formatMessage({ id: 'mobilityPlatform.content.scooter.notReserved' })} -
    + ); } return null; }; const renderLink = (linkUrl, text) => ( -
    + - + {text} - + -
    + ); - const formatRange = (range) => { + const formatRange = range => { const rangeKm = (range / 1000).toFixed(2); return `${rangeKm} km`; }; return ( -
    -
    + + {titleTypo('mobilityPlatform.content.scooter.title')} -
    -
    + +
    {singleValTypo('mobilityPlatform.content.general.provider', 'Ryde')} {renderStatus(item.is_reserved)} {singleValTypo('mobilityPlatform.content.scooter.range', formatRange(item.current_range_meters))} -
    - + + {intl.formatMessage({ id: 'mobilityPlatform.content.general.rentalUris' })} : - -
    + + {renderLink(item.rental_uris.android, 'Android')} {renderLink(item.rental_uris.ios, 'iOS')}
    -
    + ); }; ScooterInfo.propTypes = { - classes: PropTypes.objectOf(PropTypes.any).isRequired, - intl: PropTypes.objectOf(PropTypes.any).isRequired, - item: PropTypes.objectOf(PropTypes.any), + item: PropTypes.shape({ + is_reserved: PropTypes.bool, + current_range_meters: PropTypes.number, + rental_uris: PropTypes.objectOf(PropTypes.string), + }), }; ScooterInfo.defaultProps = { diff --git a/src/components/MobilityPlatform/Scooters/components/ScooterMarkers/components/ScooterInfo/__tests__/__snapshots__/ScooterInfo.test.js.snap b/src/components/MobilityPlatform/Scooters/components/ScooterMarkers/components/ScooterInfo/__tests__/__snapshots__/ScooterInfo.test.js.snap index 13721ccf1..78e23e8d7 100644 --- a/src/components/MobilityPlatform/Scooters/components/ScooterMarkers/components/ScooterInfo/__tests__/__snapshots__/ScooterInfo.test.js.snap +++ b/src/components/MobilityPlatform/Scooters/components/ScooterMarkers/components/ScooterInfo/__tests__/__snapshots__/ScooterInfo.test.js.snap @@ -3,24 +3,20 @@ exports[` should work 1`] = `
    -
    -

    - Sähköpotkulauta -

    -
    +

    + Sähköpotkulauta +

    -
    +

    should work 1`] = `

    should work 1`] = `

    should work 1`] = `

    Varauslinkit :

    should work 1`] = ` target="_blank" > diff --git a/src/components/MobilityPlatform/Scooters/components/ScooterMarkers/components/ScooterInfo/index.js b/src/components/MobilityPlatform/Scooters/components/ScooterMarkers/components/ScooterInfo/index.js index 7691081d7..a757fb947 100644 --- a/src/components/MobilityPlatform/Scooters/components/ScooterMarkers/components/ScooterInfo/index.js +++ b/src/components/MobilityPlatform/Scooters/components/ScooterMarkers/components/ScooterInfo/index.js @@ -1,6 +1,3 @@ -import { withStyles } from '@mui/styles'; -import { injectIntl } from 'react-intl'; import ScooterInfo from './ScooterInfo'; -import styles from './styles'; -export default withStyles(styles)(injectIntl(ScooterInfo)); +export default ScooterInfo; diff --git a/src/components/MobilityPlatform/Scooters/components/ScooterMarkers/components/ScooterInfo/styles.js b/src/components/MobilityPlatform/Scooters/components/ScooterMarkers/components/ScooterInfo/styles.js deleted file mode 100644 index de7e0cae1..000000000 --- a/src/components/MobilityPlatform/Scooters/components/ScooterMarkers/components/ScooterInfo/styles.js +++ /dev/null @@ -1,26 +0,0 @@ -export default theme => ({ - container: { - margin: theme.spacing(1), - }, - headerContainer: { - width: '85%', - borderBottom: '1px solid #000', - paddingBottom: theme.spacing(0.5), - }, - textContainer: { - marginTop: theme.spacing(0.5), - }, - paragraph: { - marginTop: theme.spacing(0.4), - }, - marginTop: { - marginTop: theme.spacing(0.5), - }, - bold: { - fontWeight: 'bold', - }, - link: { - color: theme.palette.link.main, - textDecoration: 'underline', - }, -}); diff --git a/src/components/MobilityPlatform/SnowPlows/components/SnowPlowsContent/SnowPlowsContent.js b/src/components/MobilityPlatform/SnowPlows/components/SnowPlowsContent/SnowPlowsContent.js index 7c5f7052f..ff592128f 100644 --- a/src/components/MobilityPlatform/SnowPlows/components/SnowPlowsContent/SnowPlowsContent.js +++ b/src/components/MobilityPlatform/SnowPlows/components/SnowPlowsContent/SnowPlowsContent.js @@ -1,36 +1,46 @@ import { Typography } from '@mui/material'; import PropTypes from 'prop-types'; import React from 'react'; +import { useIntl } from 'react-intl'; +import { StyledContainer, StyledHeaderContainer, StyledTextContainer } from '../../../styled/styled'; const SnowPlowsContent = ({ - classes, intl, formatOperation, operation, formatTime, timestamp, -}) => ( -
    -
    - - {intl.formatMessage({ - id: 'mobilityPlatform.content.streetMaintenance.title', - })} - -
    - - {intl.formatMessage({ id: 'mobilityPlatform.content.streetMaintenance' })} - : - {' '} - {formatOperation(operation)} - - - {intl.formatMessage({ id: 'mobilityPlatform.content.streetMaintenance.time' })} - : - {' '} - {formatTime(timestamp)} - -
    -); + formatOperation, operation, formatTime, timestamp, +}) => { + const intl = useIntl(); + + return ( + + + + {intl.formatMessage({ + id: 'mobilityPlatform.content.streetMaintenance.title', + })} + + +
    + + + {intl.formatMessage({ id: 'mobilityPlatform.content.streetMaintenance' })} + : + {' '} + {formatOperation(operation)} + + + + + {intl.formatMessage({ id: 'mobilityPlatform.content.streetMaintenance.time' })} + : + {' '} + {formatTime(timestamp)} + + +
    +
    + ); +}; SnowPlowsContent.propTypes = { - classes: PropTypes.objectOf(PropTypes.any).isRequired, - intl: PropTypes.objectOf(PropTypes.any).isRequired, formatOperation: PropTypes.func.isRequired, operation: PropTypes.string, formatTime: PropTypes.func.isRequired, diff --git a/src/components/MobilityPlatform/SnowPlows/components/SnowPlowsContent/index.js b/src/components/MobilityPlatform/SnowPlows/components/SnowPlowsContent/index.js index 5ea63d0b2..4bf936c7a 100644 --- a/src/components/MobilityPlatform/SnowPlows/components/SnowPlowsContent/index.js +++ b/src/components/MobilityPlatform/SnowPlows/components/SnowPlowsContent/index.js @@ -1,6 +1,3 @@ -import { withStyles } from '@mui/styles'; -import { injectIntl } from 'react-intl'; import SnowPlowsContent from './SnowPlowsContent'; -import styles from './styles'; -export default withStyles(styles)(injectIntl(SnowPlowsContent)); +export default SnowPlowsContent; diff --git a/src/components/MobilityPlatform/SnowPlows/components/SnowPlowsContent/styles.js b/src/components/MobilityPlatform/SnowPlows/components/SnowPlowsContent/styles.js deleted file mode 100644 index ba18e09ad..000000000 --- a/src/components/MobilityPlatform/SnowPlows/components/SnowPlowsContent/styles.js +++ /dev/null @@ -1,13 +0,0 @@ -const styles = theme => ({ - popupInner: { - margin: theme.spacing(2), - }, - subtitle: { - marginBottom: theme.spacing(1), - paddingBottom: theme.spacing(0.5), - borderBottom: '1px solid #000000', - width: '88%', - }, -}); - -export default styles; diff --git a/src/components/MobilityPlatform/SpeedLimitZones/components/SpeedLimitZonesContent/SpeedLimitZonesContent.js b/src/components/MobilityPlatform/SpeedLimitZones/components/SpeedLimitZonesContent/SpeedLimitZonesContent.js index 97469b6f5..33e177a98 100644 --- a/src/components/MobilityPlatform/SpeedLimitZones/components/SpeedLimitZonesContent/SpeedLimitZonesContent.js +++ b/src/components/MobilityPlatform/SpeedLimitZones/components/SpeedLimitZonesContent/SpeedLimitZonesContent.js @@ -1,28 +1,41 @@ import React from 'react'; import { PropTypes } from 'prop-types'; import { Typography } from '@mui/material'; +import { useIntl } from 'react-intl'; +import { StyledContainer, StyledHeaderContainer, StyledTextContainer } from '../../../styled/styled'; -const SpeedLimitZonesContent = ({ classes, intl, item }) => ( -
    -
    - - {intl.formatMessage({ - id: 'mobilityPlatform.content.speedLimitZones.area', - })} - -
    - - {intl.formatMessage({ - id: 'mobilityPlatform.content.speedLimitZones.limit', - }, { item: item.extra.speed_limit })} - -
    -); +const SpeedLimitZonesContent = ({ item }) => { + const intl = useIntl(); + + return ( + + + + {intl.formatMessage({ + id: 'mobilityPlatform.content.speedLimitZones.area', + })} + + +
    + + + {intl.formatMessage( + { + id: 'mobilityPlatform.content.speedLimitZones.limit', + }, + { item: item.extra.speed_limit }, + )} + + +
    +
    + ); +}; SpeedLimitZonesContent.propTypes = { - classes: PropTypes.objectOf(PropTypes.any).isRequired, - intl: PropTypes.objectOf(PropTypes.any).isRequired, - item: PropTypes.objectOf(PropTypes.any).isRequired, + item: PropTypes.shape({ + extra: PropTypes.objectOf(PropTypes.number), + }).isRequired, }; export default SpeedLimitZonesContent; diff --git a/src/components/MobilityPlatform/SpeedLimitZones/components/SpeedLimitZonesContent/__tests__/__snapshots__/SpeedLimitZonesContent.test.js.snap b/src/components/MobilityPlatform/SpeedLimitZones/components/SpeedLimitZonesContent/__tests__/__snapshots__/SpeedLimitZonesContent.test.js.snap index 3fec407d0..dd4bfea2c 100644 --- a/src/components/MobilityPlatform/SpeedLimitZones/components/SpeedLimitZonesContent/__tests__/__snapshots__/SpeedLimitZonesContent.test.js.snap +++ b/src/components/MobilityPlatform/SpeedLimitZones/components/SpeedLimitZonesContent/__tests__/__snapshots__/SpeedLimitZonesContent.test.js.snap @@ -3,10 +3,10 @@ exports[` should match snapshot 1`] = `

    should match snapshot 1`] = ` Nopeusrajoitusalue

    -

    - Nopeusrajoitus: 40 km/t -

    +
    +
    +

    + Nopeusrajoitus: 40 km/t +

    +
    +
    `; diff --git a/src/components/MobilityPlatform/SpeedLimitZones/components/SpeedLimitZonesContent/index.js b/src/components/MobilityPlatform/SpeedLimitZones/components/SpeedLimitZonesContent/index.js index 51c236f0f..8ac680b35 100644 --- a/src/components/MobilityPlatform/SpeedLimitZones/components/SpeedLimitZonesContent/index.js +++ b/src/components/MobilityPlatform/SpeedLimitZones/components/SpeedLimitZonesContent/index.js @@ -1,6 +1,3 @@ -import { withStyles } from '@mui/styles'; -import { injectIntl } from 'react-intl'; import SpeedLimitZonesContent from './SpeedLimitZonesContent'; -import styles from './styles'; -export default withStyles(styles)(injectIntl(SpeedLimitZonesContent)); +export default SpeedLimitZonesContent; diff --git a/src/components/MobilityPlatform/SpeedLimitZones/components/SpeedLimitZonesContent/styles.js b/src/components/MobilityPlatform/SpeedLimitZones/components/SpeedLimitZonesContent/styles.js deleted file mode 100644 index dcbb11119..000000000 --- a/src/components/MobilityPlatform/SpeedLimitZones/components/SpeedLimitZonesContent/styles.js +++ /dev/null @@ -1,11 +0,0 @@ -export default theme => ({ - padding: { - padding: theme.spacing(1), - }, - subtitle: { - marginBottom: theme.spacing(1), - paddingBottom: theme.spacing(0.5), - borderBottom: '1px solid #000000', - width: '88%', - }, -}); diff --git a/src/components/MobilityPlatform/TextComponent/TextComponent.js b/src/components/MobilityPlatform/TextComponent/TextComponent.js index b828fe30b..c221f2ff2 100644 --- a/src/components/MobilityPlatform/TextComponent/TextComponent.js +++ b/src/components/MobilityPlatform/TextComponent/TextComponent.js @@ -21,7 +21,7 @@ const TextComponent = ({ }; const StyledContainer = styled.div(({ theme }) => ({ - margin: theme.spacing(0.4), + marginTop: theme.spacing(0.5), })); TextComponent.propTypes = { diff --git a/src/components/MobilityPlatform/TextComponent/__tests__/__snapshots__/TextComponent.test.js.snap b/src/components/MobilityPlatform/TextComponent/__tests__/__snapshots__/TextComponent.test.js.snap index b3e11906f..73fd4c70b 100644 --- a/src/components/MobilityPlatform/TextComponent/__tests__/__snapshots__/TextComponent.test.js.snap +++ b/src/components/MobilityPlatform/TextComponent/__tests__/__snapshots__/TextComponent.test.js.snap @@ -3,7 +3,7 @@ exports[` should match snapshot 1`] = `

    { + const intl = useIntl(); -const TextContent = ({ - classes, intl, titleId, translationId, -}) => { const singleValTypo = (messageId, isTitle) => ( -

    - - {intl.formatMessage({ - id: messageId, - })} - -
    + + {intl.formatMessage({ + id: messageId, + })} + ); return ( -
    -
    + + {singleValTypo(titleId, true)} -
    -
    + + {singleValTypo(translationId, false)} -
    -
    + + ); }; TextContent.propTypes = { - classes: PropTypes.objectOf(PropTypes.any).isRequired, - intl: PropTypes.objectOf(PropTypes.any).isRequired, titleId: PropTypes.string, translationId: PropTypes.string, }; diff --git a/src/components/MobilityPlatform/TextContent/__tests__/__snapshots__/TextContent.test.js.snap b/src/components/MobilityPlatform/TextContent/__tests__/__snapshots__/TextContent.test.js.snap index 09af7b574..52229d5af 100644 --- a/src/components/MobilityPlatform/TextContent/__tests__/__snapshots__/TextContent.test.js.snap +++ b/src/components/MobilityPlatform/TextContent/__tests__/__snapshots__/TextContent.test.js.snap @@ -3,29 +3,25 @@ exports[` should work 1`] = `
    -
    -

    - Pysäköintikieltoalue -

    -
    +

    + Pysäköintikieltoalue +

    -
    -

    - Sähköpotkulautojen pysäköinti kartalla näkyville alueille on kielletty. -

    -
    +

    + Sähköpotkulautojen pysäköinti kartalla näkyville alueille on kielletty. +

    diff --git a/src/components/MobilityPlatform/TextContent/index.js b/src/components/MobilityPlatform/TextContent/index.js index 0ceb0933e..e03bb9b65 100644 --- a/src/components/MobilityPlatform/TextContent/index.js +++ b/src/components/MobilityPlatform/TextContent/index.js @@ -1,6 +1,3 @@ -import { withStyles } from '@mui/styles'; -import { injectIntl } from 'react-intl'; -import styles from './styles'; import TextContent from './TextContent'; -export default withStyles(styles)(injectIntl(TextContent)); +export default TextContent; diff --git a/src/components/MobilityPlatform/TextContent/styles.js b/src/components/MobilityPlatform/TextContent/styles.js deleted file mode 100644 index 6818cfb53..000000000 --- a/src/components/MobilityPlatform/TextContent/styles.js +++ /dev/null @@ -1,20 +0,0 @@ -export default theme => ({ - container: { - margin: theme.spacing(1), - }, - headerContainer: { - width: '85%', - borderBottom: '1px solid #000', - paddingBottom: theme.spacing(0.5), - }, - textContainer: { - marginTop: theme.spacing(0.5), - }, - contentInner: { - marginLeft: theme.spacing(1), - marginBottom: theme.spacing(1), - }, - margin: { - margin: theme.spacing(0.4), - }, -}); diff --git a/src/components/MobilityPlatform/mobilityPlatformRequests/mobilityPlatformRequests.js b/src/components/MobilityPlatform/mobilityPlatformRequests/mobilityPlatformRequests.js index 72a31ee32..15e29f29a 100644 --- a/src/components/MobilityPlatform/mobilityPlatformRequests/mobilityPlatformRequests.js +++ b/src/components/MobilityPlatform/mobilityPlatformRequests/mobilityPlatformRequests.js @@ -8,6 +8,17 @@ const isApiUrl = !apiUrl || apiUrl === 'undefined' ? null : apiUrl; const railwaysApiUrl = config.railwaysAPI; const isRailwaysApiUrl = !railwaysApiUrl || railwaysApiUrl === 'undefined' ? null : railwaysApiUrl; +const serviceMapApiUrlBase = config.serviceMapAPI.root; +const serviceMapApiUrlVersion = config.serviceMapAPI.version; +const serviceMapApiUrl = `${serviceMapApiUrlBase}${serviceMapApiUrlVersion}`; +const isServiceMapApiUrl = !serviceMapApiUrlBase || serviceMapApiUrlBase === 'undefined' ? null : serviceMapApiUrl; + +const mobilityTestApiUrl = config.mobilityTestAPI; +const isMobilityTestApiUrl = !mobilityTestApiUrl || mobilityTestApiUrl === 'undefined' ? null : mobilityTestApiUrl; + +const roadworksApiUrl = config.roadworksAPI; +const isRoadworksApiUrl = !roadworksApiUrl || roadworksApiUrl === 'undefined' ? null : roadworksApiUrl; + /** * Returns query options as a search params for URLs * @param {Object} options @@ -27,10 +38,10 @@ const optionsToParams = options => { return params.toString(); }; -const fetchMobilityMapData = async (options, setData) => { +const fetchMobilityMapData = async (options, setData, signal) => { const params = optionsToParams(options); try { - const response = await fetch(`${isApiUrl}/mobility_data/mobile_units?${params}`); + const response = await fetch(`${isApiUrl}/mobility_data/mobile_units?${params}`, { signal }); const jsonData = await response.json(); setData(jsonData.results); } catch (err) { @@ -38,9 +49,9 @@ const fetchMobilityMapData = async (options, setData) => { } }; -const fetchCultureRouteNames = async setData => { +const fetchCultureRouteNames = async (setData, signal) => { try { - const response = await fetch(`${isApiUrl}/mobility_data/mobile_unit_groups/`); + const response = await fetch(`${isApiUrl}/mobility_data/mobile_unit_groups/`, { signal }); const jsonData = await response.json(); setData(jsonData.results); } catch (err) { @@ -48,9 +59,9 @@ const fetchCultureRouteNames = async setData => { } }; -const fetchBicycleRouteNames = async setData => { +const fetchBicycleRouteNames = async (setData, signal) => { try { - const response = await fetch(`${isApiUrl}/bicycle_network/bicycle_networks/`); + const response = await fetch(`${isApiUrl}/bicycle_network/bicycle_networks/`, { signal }); const jsonData = await response.json(); setData(jsonData.results); } catch (err) { @@ -58,9 +69,11 @@ const fetchBicycleRouteNames = async setData => { } }; -const fetchBicycleRoutesGeometry = async setData => { +const fetchBicycleRoutesGeometry = async (setData, signal) => { try { - const response = await fetch(`${isApiUrl}/bicycle_network/bicycle_networkparts/?page_size=1000&latlon=true`); + const response = await fetch(`${isApiUrl}/bicycle_network/bicycle_networkparts/?page_size=1000&latlon=true`, { + signal, + }); const jsonData = await response.json(); setData(jsonData.results); } catch (err) { @@ -68,63 +81,87 @@ const fetchBicycleRoutesGeometry = async setData => { } }; -const fetchIotData = async (sourceName, setData, isScooter) => { +const fetchIotData = async (sourceName, setData, signal) => { try { - const response = await fetch(`${isApiUrl}/iot?source_name=${sourceName}`); + const response = await fetch(`${isApiUrl}/iot?source_name=${sourceName}`, { signal }); const jsonData = await response.json(); - setData(!isScooter ? jsonData.results[0].data : jsonData.results[0].data.data.bikes); + setData(jsonData.results[0].data); } catch (err) { console.warn(err.message); } }; -const fetchCityBikesData = async (sourceName, setData) => { +const fetchStreetMaintenanceData = async (endpoint, setData) => { try { - const response = await fetch(`${isApiUrl}/iot?source_name=${sourceName}`); + const response = await fetch(`${isApiUrl}/street_maintenance/${endpoint}`); const jsonData = await response.json(); - setData(jsonData.results[0].data.data.stations); + setData(jsonData.results); } catch (err) { console.warn(err.message); } }; -const fetchStreetMaintenanceData = async (endpoint, setData) => { +const fetchAreaGeometries = async (endpoint, setData, setError, signal) => { try { - const response = await fetch(`${isApiUrl}/street_maintenance/${endpoint}`); + const response = await fetch(endpoint, { signal }); const jsonData = await response.json(); - setData(jsonData.results); + setData(jsonData.features); } catch (err) { + setError(true); console.warn(err.message); } }; -const fetchParkingAreaGeometries = async (endpoint, setData, setError) => { +const fetchParkingAreaStats = async (endpoint, setData, setError, signal) => { try { - const response = await fetch(endpoint); + const response = await fetch(endpoint, { signal }); const jsonData = await response.json(); - setData(jsonData.features); + setData(jsonData.results); } catch (err) { setError(true); console.warn(err.message); } }; -const fetchParkingAreaStats = async (endpoint, setData, setError) => { +const fetchRailwaysData = async (endpoint, setData, signal) => { try { - const response = await fetch(endpoint); + const response = await fetch(`${isRailwaysApiUrl}/${endpoint}`, { signal }); + const jsonData = await response.json(); + setData(jsonData); + } catch (err) { + console.warn(err.message); + } +}; + +const fetchRoadworksData = async (options, setData, signal) => { + const params = optionsToParams(options); + try { + const response = await fetch(`${isRoadworksApiUrl}/situation/?${params}`, { signal }); const jsonData = await response.json(); setData(jsonData.results); } catch (err) { - setError(true); console.warn(err.message); } }; -const fetchRailwaysData = async (endpoint, setData) => { +const fetchPostCodeAreas = async (setData, signal) => { try { - const response = await fetch(`${isRailwaysApiUrl}/${endpoint}`); + const response = await fetch( + `${isServiceMapApiUrl}/administrative_division/?type=postcode_area&geometry=true&page_size=100`, + { signal }, + ); const jsonData = await response.json(); - setData(jsonData); + setData(jsonData.results); + } catch (err) { + console.warn(err.message); + } +}; + +const fetchMobilityProfilesData = async (setData, signal) => { + try { + const response = await fetch(`${isMobilityTestApiUrl}/?page_size=300`, { signal }); + const jsonData = await response.json(); + setData(jsonData.results); } catch (err) { console.warn(err.message); } @@ -136,9 +173,11 @@ export { fetchBicycleRouteNames, fetchBicycleRoutesGeometry, fetchIotData, - fetchCityBikesData, fetchStreetMaintenanceData, - fetchParkingAreaGeometries, + fetchAreaGeometries, fetchParkingAreaStats, fetchRailwaysData, + fetchRoadworksData, + fetchPostCodeAreas, + fetchMobilityProfilesData, }; diff --git a/src/components/MobilityPlatform/styled/styled.js b/src/components/MobilityPlatform/styled/styled.js index 73440a6c3..0b09a56be 100644 --- a/src/components/MobilityPlatform/styled/styled.js +++ b/src/components/MobilityPlatform/styled/styled.js @@ -1,9 +1,17 @@ import styled from '@emotion/styled'; +import { Typography } from '@mui/material'; + +const StyledPopupWrapper = styled.div(({ theme }) => ({ + position: 'absolute', + textAlign: 'center', + marginBottom: theme.spacing(2), + width: '429px', +})); const StyledPopupInner = styled.div(({ theme }) => ({ borderRadius: '3px', - marginBottom: theme.spacing(1), - marginLeft: theme.spacing(1.2), + marginBottom: theme.spacing(0.5), + marginLeft: theme.spacing(0.8), lineHeight: 1.2, overflowX: 'hidden', })); @@ -19,4 +27,52 @@ const StyledContentHeader = styled.div(({ theme }) => ({ width: '95%', })); -export { StyledPopupInner, StyledContentHeader }; +const StyledContainer = styled.div(({ theme }) => ({ + margin: theme.spacing(1), +})); + +const StyledHeaderContainer = styled.div(({ theme }) => ({ + width: '85%', + borderBottom: '1px solid #000', + paddingBottom: theme.spacing(0.5), +})); + +const StyledFlexContainer = styled.div(({ theme }) => ({ + display: 'flex', + flexDirection: 'row', + justifyContent: 'space-between', + alignItems: 'center', + marginTop: theme.spacing(0.5), + marginBottom: theme.spacing(0.75), + width: '93%', +})); + +const StyledTextContainer = styled.div(({ theme }) => ({ + marginTop: theme.spacing(0.5), +})); + +const StyledMargin = styled.div(({ theme }) => ({ + margin: theme.spacing(0.4), +})); + +const StyledLinkText = styled(Typography)(({ theme }) => ({ + color: theme.palette.link.main, + textDecoration: 'underline', +})); + +const StyledBoldText = styled(Typography)(() => ({ + fontWeight: 'bold', +})); + +export { + StyledPopupWrapper, + StyledPopupInner, + StyledContentHeader, + StyledContainer, + StyledHeaderContainer, + StyledFlexContainer, + StyledTextContainer, + StyledMargin, + StyledLinkText, + StyledBoldText, +}; diff --git a/src/components/MobilityPlatform/utils/useIotDataFetch.js b/src/components/MobilityPlatform/utils/useIotDataFetch.js new file mode 100644 index 000000000..7c49006c3 --- /dev/null +++ b/src/components/MobilityPlatform/utils/useIotDataFetch.js @@ -0,0 +1,38 @@ +import { useState, useEffect } from 'react'; +import { fetchIotData } from '../mobilityPlatformRequests/mobilityPlatformRequests'; + +const useIotDataFetch = (source, showData, embedded) => { + const [data, setData] = useState([]); + + useEffect(() => { + const controller = new AbortController(); + const { signal } = controller; + if (showData || embedded) { + fetchIotData(source, setData, signal); + } + return () => controller.abort(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [showData, embedded]); + + const setDataBySource = () => { + if (source === 'SDR') { + return data?.data?.bikes; + } + if (source === 'CBI' || source === 'CBS') { + return data?.data?.stations; + } + if (source === 'FAA') { + return data?.flights?.arr?.body?.flight; + } + if (source === 'FAD') { + return data?.flights?.dep?.body?.flight; + } + return data; + }; + + const iotData = setDataBySource(); + + return { iotData }; +}; + +export default useIotDataFetch; diff --git a/src/components/MobilityPlatform/utils/useMobilityDataFetch.js b/src/components/MobilityPlatform/utils/useMobilityDataFetch.js index 9c4e5f3bc..cfac82102 100644 --- a/src/components/MobilityPlatform/utils/useMobilityDataFetch.js +++ b/src/components/MobilityPlatform/utils/useMobilityDataFetch.js @@ -5,9 +5,12 @@ const useMobilityDataFetch = (options, showData, embedded) => { const [data, setData] = useState([]); useEffect(() => { + const controller = new AbortController(); + const { signal } = controller; if (showData || embedded) { - fetchMobilityMapData(options, setData); + fetchMobilityMapData(options, setData, signal); } + return () => controller.abort(); // eslint-disable-next-line react-hooks/exhaustive-deps }, [showData, embedded]); diff --git a/src/components/MobilityPlatform/utils/useRoadworksDataFetch.js b/src/components/MobilityPlatform/utils/useRoadworksDataFetch.js new file mode 100644 index 000000000..0f46c85f7 --- /dev/null +++ b/src/components/MobilityPlatform/utils/useRoadworksDataFetch.js @@ -0,0 +1,20 @@ +import { useState, useEffect } from 'react'; +import { fetchRoadworksData } from '../mobilityPlatformRequests/mobilityPlatformRequests'; + +const useRoadworksDataFetch = (options, showData) => { + const [data, setData] = useState([]); + + useEffect(() => { + const controller = new AbortController(); + const { signal } = controller; + if (showData) { + fetchRoadworksData(options, setData, signal); + } + return () => controller.abort(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [showData]); + + return { data }; +}; + +export default useRoadworksDataFetch; diff --git a/src/components/MobilityPlatform/utils/utils.js b/src/components/MobilityPlatform/utils/utils.js index b16ab830d..1e26a51d7 100644 --- a/src/components/MobilityPlatform/utils/utils.js +++ b/src/components/MobilityPlatform/utils/utils.js @@ -14,9 +14,9 @@ const isDataValid = (visibilityValue, data) => visibilityValue && data && data.l */ const isObjValid = (visibilityValue, obj) => visibilityValue && obj && Object.entries(obj).length > 0; -const createIcon = (icon) => ({ +const createIcon = (icon, isSmall) => ({ iconUrl: icon, - iconSize: [45, 45], + iconSize: isSmall ? [35, 35] : [45, 45], }); const whiteOptionsBase = (attrs = {}) => ({ color: 'rgba(255, 255, 255, 255)', ...attrs }); @@ -24,6 +24,7 @@ const blackOptionsBase = (attrs = {}) => ({ color: 'rgba(0, 0, 0, 255)', ...attr const blueOptionsBase = (attrs = {}) => ({ color: 'rgba(7, 44, 115, 255)', ...attrs }); const redOptionsBase = (attrs = {}) => ({ color: 'rgba(251, 5, 21, 255)', ...attrs }); const grayOptionsBase = (attrs = {}) => ({ color: 'rgba(64, 64, 64, 255)', ...attrs }); +const greenOptionsBase = (attrs = {}) => ({ color: 'rgba(15, 115, 6, 255)', ...attrs }); /** * Return arrays of coordinates that fit markers inside map bounds @@ -35,7 +36,7 @@ const grayOptionsBase = (attrs = {}) => ({ color: 'rgba(64, 64, 64, 255)', ...at const fitToMapBounds = (renderData, data, map) => { if (renderData) { const bounds = []; - data.forEach((item) => { + data.forEach(item => { bounds.push([item.geometry_coords.lat, item.geometry_coords.lon]); }); map.fitBounds(bounds); @@ -52,7 +53,7 @@ const fitToMapBounds = (renderData, data, map) => { const fitPolygonsToBounds = (renderData, data, map) => { if (renderData) { const bounds = []; - data.forEach((item) => { + data.forEach(item => { bounds.push(item.geometry_coords); }); map.fitBounds(bounds); @@ -100,6 +101,7 @@ export { blueOptionsBase, redOptionsBase, grayOptionsBase, + greenOptionsBase, fitToMapBounds, fitPolygonsToBounds, setRender, diff --git a/src/components/SearchBar/createSuggestions.js b/src/components/SearchBar/createSuggestions.js index 07fc27651..aeb4c270b 100644 --- a/src/components/SearchBar/createSuggestions.js +++ b/src/components/SearchBar/createSuggestions.js @@ -1,4 +1,5 @@ import ServiceMapAPI from '../../utils/newFetch/ServiceMapAPI'; +import config from '../../../config'; const createSuggestions = ( query, @@ -15,6 +16,7 @@ const createSuggestions = ( const addressLimit = 1; const servicenodeLimit = 10; const pageSize = unitLimit + serviceLimit + addressLimit + servicenodeLimit; + const municipalities = citySettings?.length ? citySettings?.join(',') : config.cities; const additionalOptions = { page_size: pageSize, @@ -23,6 +25,7 @@ const createSuggestions = ( service_limit: serviceLimit, address_limit: addressLimit, servicenode_limit: servicenodeLimit, + municipality: municipalities, language: locale, }; @@ -32,10 +35,10 @@ const createSuggestions = ( // Filter services with city settings if (citySettings.length) { - filteredResults = filteredResults.filter((result) => { + filteredResults = filteredResults.filter(result => { if (result.object_type === 'service' || result.object_type === 'servicenode') { let totalResultCount = 0; - citySettings.forEach((city) => { + citySettings.forEach(city => { if (result.unit_count?.municipality[city]) { totalResultCount += result.unit_count.municipality[city]; } @@ -46,9 +49,8 @@ const createSuggestions = ( }); } - // Handle address results - filteredResults.forEach((item) => { + filteredResults.forEach(item => { if (item.object_type === 'address') { if (getLocaleText(item.name).toLowerCase() !== query.toLowerCase()) { item.name = item.street.name; diff --git a/src/context/MobilityPlatformContext.js b/src/context/MobilityPlatformContext.js index 2c1713146..5c2a12168 100644 --- a/src/context/MobilityPlatformContext.js +++ b/src/context/MobilityPlatformContext.js @@ -22,6 +22,12 @@ const trafficCountersInitial = { driving: false, }; +const accessibilityAreasInitial = { + all: false, + walking: false, + cycling: false, +}; + const MobilityPlatformContextProvider = ({ children }) => { // Check if mobility platform is open or not const [openMobilityPlatform, setOpenMobilityPlatform] = useState(false); @@ -93,6 +99,11 @@ const MobilityPlatformContextProvider = ({ children }) => { // public transport const [showBusStops, setShowBusStops] = useState(false); const [showRailwayStations, setShowRailwayStations] = useState(false); + const [showAirports, setShowAirports] = useState(false); + + // units + const [showAccessibilityAreas, setShowAccessibilityAreas] = useState(accessibilityAreasInitial); + const [accessibilityAreasData, setAccessibilityAreasData] = useState([]); // other const [showOutdoorGymDevices, setShowOutdoorGymDevices] = useState(false); @@ -104,6 +115,7 @@ const MobilityPlatformContextProvider = ({ children }) => { const [showPublicBenches, setShowPublicBenches] = useState(false); const [showRoadworks, setShowRoadworks] = useState(false); const [showBarbecuePlaces, setShowBarbecuePlaces] = useState(false); + const [showMobilityResults, setShowMobilityResults] = useState(false); const getters = { openMobilityPlatform, @@ -164,6 +176,10 @@ const MobilityPlatformContextProvider = ({ children }) => { // public transport showBusStops, showRailwayStations, + showAirports, + // units + showAccessibilityAreas, + accessibilityAreasData, // other showOutdoorGymDevices, showPublicToilets, @@ -174,6 +190,7 @@ const MobilityPlatformContextProvider = ({ children }) => { showPublicBenches, showRoadworks, showBarbecuePlaces, + showMobilityResults, }; const setters = { @@ -235,6 +252,10 @@ const MobilityPlatformContextProvider = ({ children }) => { // public transport setShowBusStops, setShowRailwayStations, + setShowAirports, + // units + setShowAccessibilityAreas, + setAccessibilityAreasData, // other setShowOutdoorGymDevices, setShowPublicToilets, @@ -245,6 +266,7 @@ const MobilityPlatformContextProvider = ({ children }) => { setShowPublicBenches, setShowRoadworks, setShowBarbecuePlaces, + setShowMobilityResults, }; const contextValues = { ...getters, ...setters }; diff --git a/src/i18n/en.js b/src/i18n/en.js index 3efcd5543..59661f9fb 100644 --- a/src/i18n/en.js +++ b/src/i18n/en.js @@ -164,6 +164,12 @@ const translations = { 'area.list.statistic.30-49': 'Age 30-49 years', 'area.list.statistic.50-64': 'Age 50-64 years', 'area.list.statistic.65+': 'Age over 64 years', + 'area.tab.mobilityTest.results': 'Mobility profiles of Turku region', + 'area.mobilityResults.toggle': 'Mobility profiles', + 'area.mobilityResults.postCodeArea': 'Postcode area: {value}', + 'area.mobilityResults.empty': 'No mobility profiles were found.', + 'area.mobilityResults.info.text': 'The map shows general information about mobility profiles (for example Hassle-free Hare) in the Turku region divided by postcode areas. The information will be displayed when there are at least five responses in each profile. You can find out your mobility profile by taking the Turku mobility survey.', + 'area.mobilityResults.link.text': 'liikkumistesti.turku.fi (new tab)', 'parkingArea.popup.residentName': 'Zone {letter}', 'parkingArea.popup.payment1': 'Free parking', @@ -236,6 +242,9 @@ const translations = { 'embedder.preview.title': 'Map preview', 'embedder.options.title': 'Show on the map', 'embedder.options.label.units': 'Show service points', + 'embedder.options.label.units.accessibilityAreas': 'Show accessibility zones', + 'embedder.options.label.units.accessibilityAreas.walk': 'Show accessibility zones (walking)', + 'embedder.options.label.units.accessibilityAreas.bicycle': 'Show accessibility zones (cycling)', 'embedder.options.list.title': 'List of service points', 'embedder.options.label.list.none': 'Hide the list of service points', 'embedder.options.label.list.side': 'Show service points as a list (beside the map)', @@ -461,7 +470,7 @@ const translations = { 'unit.details.notFound': 'Service point info not available.', 'unit.plural': 'Service points', 'unit.distance': 'Distance', - + 'unit.accessibilityAreas': 'Accessibility zones', 'unit.contact.info': 'Contact information', 'unit.links': 'Web sites', 'unit.eServices': 'Electronic services', @@ -526,6 +535,14 @@ const translations = { 'unit.outdoorLink': 'Check the condition of an exercise location in the ulkoliikunta.fi service', 'unit.seo.description': 'View service point on the map', 'unit.seo.description.accessibility': 'View accessibility info and service point on the map', + 'unit.accessibilityAreas.title': 'Accessibility zones of schools and daycares', + 'unit.accessibilityAreas.description': 'Accessibility zones indicate how long it takes to reach a destination by walking or cycling. The times are calculated based on average speeds of children (walking 4 km/h, cycling 10 km/h).', + 'unit.accessibilityAreas.all.label': 'All accessibility zones', + 'unit.accessibilityAreas.walking.label': 'Accessibility zones (walking)', + 'unit.accessibilityAreas.cycling.label': 'Accessibility zones (cycling)', + 'unit.accessibilityAreas.content.subtitle': 'Accessibility zone:', + 'unit.accessibilityAreas.content.transport': 'Mode of transport: {value}', + 'unit.accessibilityAreas.content.duration': 'Estimated duration: {value} minutes', // Search 'search': 'Search', @@ -744,7 +761,7 @@ const translations = { 'mobilityPlatform.menu.routes.info': 'You can select a route from the list below.', 'mobilityPlatform.menu.routes.emptyList': 'Routes are loading.', 'mobilityPlatform.menu.bicycleRoutes.title': 'Info about the route.', - 'mobilityPlatform.menu.bicycleRoutes.length': 'Route length:', + 'mobilityPlatform.menu.bicycleRoutes.length': 'Route length: {value} km.', 'mobilityPlatform.menu.routes.name': 'Route name', 'mobilityPlatform.menu.showRentalCars': 'Shared use cars', 'mobilityPlatform.menu.showParkingSpaces': 'Parking spaces', @@ -803,6 +820,7 @@ const translations = { 'mobilityPlatform.menu.show.airMonitoring': 'Air quality stations', 'mobilityPlatform.menu.show.parkAndRideBikes': 'Park and ride stops for bicycles', 'mobilityPlatform.menu.show.barbecuePlaces': 'Sites for barbequing & making fire', + 'mobilityPlatform.menu.show.airPorts': 'Airport', // Content 'mobilityPlatform.content.general.provider': 'Service provider: {value}', @@ -852,11 +870,11 @@ const translations = { 'mobilityPlatform.content.parkingSpaces.type': 'Payment type', 'mobilityPlatform.content.parkingSpaces.paid': 'Toll parking', 'mobilityPlatform.content.parkingSpaces.empty': 'No vacant spaces left', - 'mobilityPlatform.content.parkingChargeZones.zone': 'Zone', - 'mobilityPlatform.content.parkingChargeZones.price': 'Charge', - 'mobilityPlatform.content.parkingChargeZones.price.weekDays': 'Toll charge on workdays', - 'mobilityPlatform.content.parkingChargeZones.price.saturday': 'Toll charge on saturdays', - 'mobilityPlatform.content.parkingChargeZones.price.sunday': 'Toll charge on sundays', + 'mobilityPlatform.content.parkingChargeZones.zone': 'Zone: {value}', + 'mobilityPlatform.content.parkingChargeZones.price': 'Charge: {value}', + 'mobilityPlatform.content.parkingChargeZones.price.weekDays': 'Toll charge on workdays: {value}', + 'mobilityPlatform.content.parkingChargeZones.price.saturday': 'Toll charge on saturdays: {value}', + 'mobilityPlatform.content.parkingChargeZones.price.sunday': 'Toll charge on sundays: {value}', 'mobilityPlatform.content.description.notAvailable': 'Description text is not available.', 'mobilityPlatform.content.cityBikes.title': 'City bike station', 'mobilityPlatform.content.cityBikes.name': 'Station: {value}', @@ -899,17 +917,31 @@ const translations = { 'mobilityPlatform.content.publicParking.totalTime': 'Parking time limit: {value} hours', 'mobilityPlatform.content.parkingMachine.title': 'Parking machine', 'mobilityPlatform.content.parkingMachine.location': 'Location: {value}', - 'mobilityPlatform.content.parkingMachine.payment': 'Charge: {value} €/h', + 'mobilityPlatform.content.parkingMachine.payment': 'Charge: {value}', 'mobilityPlatform.content.parkingMachine.paymentTypes': 'Payment methods: {value}', 'mobilityPlatform.content.parkingMachine.otherInfo': 'Additional info: {value}', 'mobilityPlatform.content.crosswalks.title': 'Crosswalk', 'mobilityPlatform.content.railways.train': 'Train: {value1} {value2}', - 'mobilityPlatform.content.departingTrains.title': 'Departing trains', - 'mobilityPlatform.content.arrivingTrains.title': 'Incoming trains', + 'mobilityPlatform.content.departingTrains.title': 'Departing trains:', + 'mobilityPlatform.content.arrivingTrains.title': 'Incoming trains:', 'mobilityPlatform.content.departingTrains.empty': 'No departing trains', 'mobilityPlatform.content.arrivingTrains.empty': 'No incoming trains', 'mobilityPlatform.parkAndRide.content.subtitle': 'Park and ride stop for bicycles', 'mobilityPlatform.content.barbecuePlace.title': 'Site for barbequing or making fire', + 'mobilityPlatform.content.barbecuePlace.manufacturer': 'Manufacturer: {value}', + 'mobilityPlatform.content.barbecuePlace.model': 'Model: {value}', + 'mobilityPlatform.content.airport.title': 'Turku airport', + 'mobilityPlatform.content.airport.arrivals': 'Incoming flights:', + 'mobilityPlatform.content.airport.departees': 'Departing flights:', + 'mobilityPlatform.content.airport.departees.empty': 'No departing flights.', + 'mobilityPlatform.content.airport.arrivals.empty': 'No incoming flights.', + 'mobilityPlatform.content.airport.cities.turku': 'To Turku', + 'mobilityPlatform.content.airport.cities.stockholm': 'To Stockholm', + 'mobilityPlatform.content.airport.cities.mariehamn': 'To Mariehamn', + 'mobilityPlatform.content.airport.cities.riga': 'To Riga', + 'mobilityPlatform.content.airport.cities.gdansk': 'To Gdańsk', + 'mobilityPlatform.content.airport.cities.rome': 'To Rome', + 'mobilityPlatform.content.airport.cities.rhodes': 'To Rhodes', // Info text 'mobilityPlatform.info.description.title': 'Route description', @@ -971,6 +1003,7 @@ const translations = { 'mobilityPlatform.info.airMonitoring.link': 'For more information visit https://en.ilmatieteenlaitos.fi/air-quality', 'mobilityPlatform.info.parkAndRideBicycles': 'Park-and-ride arrangements provide the opportunity to leave your bicycle parked safely and hop on a bus to continue your journey. The Föli area boasts many park-and-ride sites for bicycles. Park-and-ride parking is free and intended for those using public transport for connections.', 'mobilityPlatform.info.barbecuePlaces': 'The map shows official sites for barbequing or making fire. Making a fire on the land administered by Turku City is allowed only on places designated for making an open flame. Making a fire on any other place than the official campfire and barbeque sites is always forbidden.', + 'mobilityPlatform.info.airport': 'The map shows Turku airport and information about flights for the current day. Flight traffic has been divided into incoming and departing flights. The data comes from the interface provided by Finavia.', // Bicycle routes 'mobilityPlatform.menu.bicycleRoutes.euroVelo': 'The EuroVelo 10, is the European cycle route that stretches along the Finnish costal line. The distance between Helsinki and Turku has roadside directions for the route.', diff --git a/src/i18n/fi.js b/src/i18n/fi.js index c80337b96..28d029abc 100644 --- a/src/i18n/fi.js +++ b/src/i18n/fi.js @@ -167,6 +167,12 @@ const translations = { 'area.list.statistic.30-49': 'Ikä 30-49 vuotta', 'area.list.statistic.50-64': 'Ikä 50-64 vuotta', 'area.list.statistic.65+': 'Ikä yli 65 vuotta', + 'area.tab.mobilityTest.results': 'Turun alueen liikkumisprofiilit', + 'area.mobilityResults.toggle': 'Liikkumisprofiilit', + 'area.mobilityResults.postCodeArea': 'Postinumeroalue: {value}', + 'area.mobilityResults.empty': 'Liikkumisprofiileja ei löytynyt.', + 'area.mobilityResults.info.text': 'Kartalla näet tietoa Turun alueen liikkumisprofiileista (esimerkiksi Joustava Jänis) postinumeroalueittain jaettuna. Tiedot tulevat näkyviin, kun kussakin profiilissa on vähintään viisi vastausta. Vastaamalla Turun liikkumistestiin, voit selvittää millainen liikkuja olet.', + 'area.mobilityResults.link.text': 'liikkumistesti.turku.fi (uusi välilehti)', 'parkingArea.popup.residentName': 'Alue {letter}', 'parkingArea.popup.payment1': 'Ilmainen pysäköinti', @@ -238,6 +244,9 @@ const translations = { 'embedder.map.aria.label': 'Valitse taustakartta', 'embedder.options.title': 'Näytä kartalla', 'embedder.options.label.units': 'Näytä toimipisteet', + 'embedder.options.label.units.accessibilityAreas': 'Näytä lähestymisalueet', + 'embedder.options.label.units.accessibilityAreas.walk': 'Näytä lähestymisalueet (kävely)', + 'embedder.options.label.units.accessibilityAreas.bicycle': 'Näytä lähestymisalueet (pyöräily)', 'embedder.options.list.title': 'Toimipistelista', 'embedder.options.label.list.none': 'Piilota toimipistelista', 'embedder.options.label.list.side': 'Näytä toimipisteet listana (kartan vieressä)', @@ -464,7 +473,7 @@ const translations = { 'unit.details.notFound': 'Toimipisteen tietoja ei saatavilla.', 'unit.plural': 'Toimipisteet', 'unit.distance': 'Etäisyys: ', - + 'unit.accessibilityAreas': 'Lähestymisalueet', 'unit.contact.info': 'Yhteystiedot', 'unit.links': 'Verkossa', 'unit.eServices': 'Sähköinen asiointi', @@ -530,6 +539,14 @@ const translations = { 'unit.outdoorLink': 'Katso liikuntapaikan kunto ulkoliikunta.fi palvelusta', 'unit.seo.description': 'Katso sijainti kartalla', 'unit.seo.description.accessibility': 'Katso esteettömyystiedot ja sijainti kartalla', + 'unit.accessibilityAreas.title': 'Koulujen ja päiväkotien lähestymisalueet', + 'unit.accessibilityAreas.description': 'Lähestymisalueet kertovat, kuinka pitkä ajallinen etäisyys kohteeseen on kävellen tai pyöräillen. Ajat on laskettu keskimääräisillä lasten nopeuksilla (kävely 4 km/h, pyöräily 10 km/h).', + 'unit.accessibilityAreas.all.label': 'Kaikki lähestymisalueet', + 'unit.accessibilityAreas.walking.label': 'Lähestymisalueet (kävely)', + 'unit.accessibilityAreas.cycling.label': 'Lähestymisalueet (pyöräily)', + 'unit.accessibilityAreas.content.subtitle': 'Lähestymisalue:', + 'unit.accessibilityAreas.content.transport': 'Kulkumuto: {value}', + 'unit.accessibilityAreas.content.duration': 'Arvioitu aika: {value} minuuttia', // Search 'search': 'Hae', @@ -749,7 +766,7 @@ const translations = { 'mobilityPlatform.menu.routes.info': 'Valitse reitti oheisesta listasta.', 'mobilityPlatform.menu.routes.emptyList': 'Reittejä ladataan.', 'mobilityPlatform.menu.bicycleRoutes.title': 'Tietoja reitistä.', - 'mobilityPlatform.menu.bicycleRoutes.length': 'Reitin pituus:', + 'mobilityPlatform.menu.bicycleRoutes.length': 'Reitin pituus: {value} km.', 'mobilityPlatform.menu.routes.name': 'Reitti', 'mobilityPlatform.menu.showRentalCars': 'Yhteiskäyttöautot', 'mobilityPlatform.menu.showParkingSpaces': 'Pysäköintialueet', @@ -808,6 +825,7 @@ const translations = { 'mobilityPlatform.menu.show.airMonitoring': 'Ilmanlaadun mittauspisteet', 'mobilityPlatform.menu.show.parkAndRideBikes': 'Liityntäpysäkit pyörille', 'mobilityPlatform.menu.show.barbecuePlaces': 'Grillaus- ja tulentekopaikat', + 'mobilityPlatform.menu.show.airPorts': 'Lentoasema', // Content 'mobilityPlatform.content.general.provider': 'Palveluntarjoaja: {value}', @@ -847,11 +865,11 @@ const translations = { 'mobilityPlatform.content.parkingSpaces.type': 'Maksutyyppi', 'mobilityPlatform.content.parkingSpaces.paid': 'Maksullinen', 'mobilityPlatform.content.parkingSpaces.empty': 'Ei vapaita paikkoja jäljellä', - 'mobilityPlatform.content.parkingChargeZones.zone': 'Vyöhyke', - 'mobilityPlatform.content.parkingChargeZones.price': 'Hinta', - 'mobilityPlatform.content.parkingChargeZones.price.weekDays': 'Maksullisuus arkisin', - 'mobilityPlatform.content.parkingChargeZones.price.saturday': 'Maksullisuus lauantaisin', - 'mobilityPlatform.content.parkingChargeZones.price.sunday': 'Maksullisuus sunnuntaisin', + 'mobilityPlatform.content.parkingChargeZones.zone': 'Vyöhyke: {value}', + 'mobilityPlatform.content.parkingChargeZones.price': 'Hinta: {value}', + 'mobilityPlatform.content.parkingChargeZones.price.weekDays': 'Maksullisuus arkisin: {value}', + 'mobilityPlatform.content.parkingChargeZones.price.saturday': 'Maksullisuus lauantaisin: {value}', + 'mobilityPlatform.content.parkingChargeZones.price.sunday': 'Maksullisuus sunnuntaisin: {value}', 'mobilityPlatform.content.description.notAvailable': 'Kuvaustekstiä ei ole saatavilla.', 'mobilityPlatform.content.cityBikes.title': 'Kaupunkipyöräasema', 'mobilityPlatform.content.cityBikes.name': 'Asema: {value}', @@ -894,17 +912,31 @@ const translations = { 'mobilityPlatform.content.publicParking.totalTime': 'Pysäköinnin enimmäisaika: {value} tuntia', 'mobilityPlatform.content.parkingMachine.title': 'Pysäköintiautomaatti', 'mobilityPlatform.content.parkingMachine.location': 'Sijainti: {value}', - 'mobilityPlatform.content.parkingMachine.payment': 'Maksu: {value} €/t', + 'mobilityPlatform.content.parkingMachine.payment': 'Maksu: {value}', 'mobilityPlatform.content.parkingMachine.paymentTypes': 'Maksutavat: {value}', 'mobilityPlatform.content.parkingMachine.otherInfo': 'Lisätietoja: {value}', 'mobilityPlatform.content.crosswalks.title': 'Suojatie', 'mobilityPlatform.content.railways.train': 'Juna: {value1} {value2}', - 'mobilityPlatform.content.departingTrains.title': 'Lähtevät junat', - 'mobilityPlatform.content.arrivingTrains.title': 'Saapuvat junat', + 'mobilityPlatform.content.departingTrains.title': 'Lähtevät junat:', + 'mobilityPlatform.content.arrivingTrains.title': 'Saapuvat junat:', 'mobilityPlatform.content.departingTrains.empty': 'Ei lähteviä junia', 'mobilityPlatform.content.arrivingTrains.empty': 'Ei saapuvia junia', 'mobilityPlatform.parkAndRide.content.subtitle': 'Liityntäpysäkki pyörille', 'mobilityPlatform.content.barbecuePlace.title': 'Grillaus- ja tulentekopaikka', + 'mobilityPlatform.content.barbecuePlace.manufacturer': 'Valmistaja: {value}', + 'mobilityPlatform.content.barbecuePlace.model': 'Malli: {value}', + 'mobilityPlatform.content.airport.title': 'Turun lentoasema', + 'mobilityPlatform.content.airport.arrivals': 'Saapuvat lennot:', + 'mobilityPlatform.content.airport.departees': 'Lähtevät lennot:', + 'mobilityPlatform.content.airport.departees.empty': 'Ei lähteviä lentoja.', + 'mobilityPlatform.content.airport.arrivals.empty': 'Ei saapuvia lentoja.', + 'mobilityPlatform.content.airport.cities.turku': 'Turkuun', + 'mobilityPlatform.content.airport.cities.stockholm': 'Tukholmaan', + 'mobilityPlatform.content.airport.cities.mariehamn': 'Maarianhaminaan', + 'mobilityPlatform.content.airport.cities.riga': 'Riikaan', + 'mobilityPlatform.content.airport.cities.gdansk': 'Gdańskiin', + 'mobilityPlatform.content.airport.cities.rome': 'Roomaan', + 'mobilityPlatform.content.airport.cities.rhodes': 'Ródokselle', // Info text 'mobilityPlatform.info.description.title': 'Tietoja reitistä', @@ -966,6 +998,7 @@ const translations = { 'mobilityPlatform.info.airMonitoring.link': 'Lisätietoja saa osoitteesta https://www.ilmatieteenlaitos.fi/ilmanlaatu', 'mobilityPlatform.info.parkAndRideBicycles': 'Liityntäpysäköinti tarjoaa mahdollisuuden jättää oma pyörä parkkiin ja jatkaa matkaa bussilla. Föli-alueella on useita liityntäpysäköintipaikkoja pyörille. Liityntäpysäköinti on maksutonta ja tarkoitettu vain joukkoliikennettä vaihtoyhteytenä käyttäville.', 'mobilityPlatform.info.barbecuePlaces': 'Kartalla näkyvät Turun viralliset tulenteko- ja grillauspaikat. Turun kaupungin hallinnoimilla mailla tulenteko on sallittu ainoastaan avotulen tekoon tarkoitetuilla paikoilla. Muilla kuin virallisilla nuotio- ja grillauspaikoilla avotulen teko on aina kielletty.', + 'mobilityPlatform.info.airport': 'Kartalla näkyvät Turun lentoasema ja lentojen tiedot kuluvan vuorokauden ajalta. Lennot on jaettu saapuviin ja lähteviin lentoihin. Tiedot ovat peräisin Finavian rajapinnasta.', // Bicycle routes 'mobilityPlatform.menu.bicycleRoutes.euroVelo': 'EuroVelo 10 on eurooppalainen Suomen rannikkoa seuraava polkupyöräreitti. Helsingin ja Turun välisellä matkalla reitti on merkitty opastein.', diff --git a/src/i18n/sv.js b/src/i18n/sv.js index 44f034757..49c186adb 100644 --- a/src/i18n/sv.js +++ b/src/i18n/sv.js @@ -167,6 +167,12 @@ const translations = { 'area.list.statistic.30-49': 'Ålder 30-49 år', 'area.list.statistic.50-64': 'Ålder 50-64 år', 'area.list.statistic.65+': 'Ålder över 65 år', + 'area.tab.mobilityTest.results': 'Mobilitetsprofiler av Åbo område', + 'area.mobilityResults.toggle': 'Mobilitetsprofiler', + 'area.mobilityResults.postCodeArea': 'Postnummerområd: {value}', + 'area.mobilityResults.empty': 'Inga mobilitetsprofiler hittades.', + 'area.mobilityResults.info.text': 'Kartan visas information om mobilitetsprofiler (till exempel Hinderfri Hare) i Åbo-området uppdelat efter postnummerområden. Informationen visas när det finns minst fem svar i varje profil. Genom att göra Åbos mobilitetsprofiltest kan du ta reda på vilken typ av rörlig person du är.', + 'area.mobilityResults.link.text': 'liikkumistesti.turku.fi (ny flik)', // TODO: translate all 'parkingArea.popup.residentName': 'Zon {letter}', @@ -239,6 +245,9 @@ const translations = { 'embedder.map.aria.label': 'Välj bakgrundskarta', 'embedder.options.title': 'Visa på kartan', 'embedder.options.label.units': 'Visa verksamhetsställen', + 'embedder.options.label.units.accessibilityAreas': 'Visa tillgänglighetsområden', + 'embedder.options.label.units.accessibilityAreas.walk': 'Visa tillgänglighetsområden (promenad)', + 'embedder.options.label.units.accessibilityAreas.bicycle': 'Visa tillgänglighetsområden (cykling)', 'embedder.options.list.title': 'List med verksamhetsställen', 'embedder.options.label.list.none': 'Ta bort listan med verksamhetsställen', 'embedder.options.label.list.side': 'Visa verksamhetsställen som en lista (intill kartan)', @@ -465,7 +474,7 @@ const translations = { 'unit.details.notFound': 'Verksamhetsställets uppgifter finns inte att tillgå.', 'unit.plural': 'Verksamhetsställen', 'unit.distance': 'Avstånd', - + 'unit.accessibilityAreas': 'Tillgänglighetsområden', 'unit.contact.info': 'Kontaktuppgifter', 'unit.links': 'På webben', 'unit.eServices': 'E-tjänster', @@ -530,6 +539,14 @@ const translations = { 'unit.outdoorLink': 'Kolla skicket på en motionsplats i tjänsten ulkoliikunta.fi', 'unit.seo.description': 'Se läget på kartan', 'unit.seo.description.accessibility': 'Se tillgänglighetsuppgifterna och läget på kartan', + 'unit.accessibilityAreas.title': 'Tillgänglighetsområdena av skolor och daghem', + 'unit.accessibilityAreas.description': 'Tillgänglighetsområdena berättar hur lång tid det tar att gå eller cykla till destinationen. Tiderna har beräknats med genomsnittliga barnhastigheter (gång 4 km/h, cykling 10 km/h).', + 'unit.accessibilityAreas.all.label': 'Alla tillgänglighetsområdena', + 'unit.accessibilityAreas.walking.label': 'Tillgänglighetsområdena (promenad)', + 'unit.accessibilityAreas.cycling.label': 'Tillgänglighetsområdena (cykling)', + 'unit.accessibilityAreas.content.subtitle': 'Tillgänglighetsområd:', + 'unit.accessibilityAreas.content.transport': 'Transportsätt: {value}', + 'unit.accessibilityAreas.content.duration': 'Beräknad varaktighet: {value} minuter', // Search 'search': 'Sök', @@ -748,7 +765,7 @@ const translations = { 'mobilityPlatform.menu.routes.info': 'Du kan välja rutten från nedanstående listan.', 'mobilityPlatform.menu.routes.emptyList': 'Rutter håller på att laddas.', 'mobilityPlatform.menu.bicycleRoutes.title': 'Information om rutten.', - 'mobilityPlatform.menu.bicycleRoutes.length': 'Ruttlängd:', + 'mobilityPlatform.menu.bicycleRoutes.length': 'Ruttlängd: {value} km.', 'mobilityPlatform.menu.routes.name': 'Rutt', 'mobilityPlatform.menu.showRentalCars': 'Bil för delad användning', 'mobilityPlatform.menu.showParkingSpaces': 'Parkeringsplatser', @@ -807,6 +824,7 @@ const translations = { 'mobilityPlatform.menu.show.railwayStations': 'Järnvägsstationer', 'mobilityPlatform.menu.show.parkAndRideBikes': 'Infartsparkering för cyklar', 'mobilityPlatform.menu.show.barbecuePlaces': 'Grill- och eldningsplatser', + 'mobilityPlatform.menu.show.airPorts': 'Flygplats', // Content 'mobilityPlatform.content.general.provider': 'Tjänsteleverantör: {value}', @@ -856,11 +874,11 @@ const translations = { 'mobilityPlatform.content.parkingSpaces.type': 'Typ av betalning', 'mobilityPlatform.content.parkingSpaces.paid': 'Avgiftsbelagd', 'mobilityPlatform.content.parkingSpaces.empty': 'Inga lediga platser kvar: {value} / {capacity}', - 'mobilityPlatform.content.parkingChargeZones.zone': 'Zon', - 'mobilityPlatform.content.parkingChargeZones.price': 'Avgift', - 'mobilityPlatform.content.parkingChargeZones.price.weekDays': 'Avgiftsbelagd vardagar', - 'mobilityPlatform.content.parkingChargeZones.price.saturday': 'Avgiftsbelagd lördagar', - 'mobilityPlatform.content.parkingChargeZones.price.sunday': 'Avgiftsbelagd söndagar', + 'mobilityPlatform.content.parkingChargeZones.zone': 'Zon: {value}', + 'mobilityPlatform.content.parkingChargeZones.price': 'Avgift: {value}', + 'mobilityPlatform.content.parkingChargeZones.price.weekDays': 'Avgiftsbelagd vardagar: {value}', + 'mobilityPlatform.content.parkingChargeZones.price.saturday': 'Avgiftsbelagd lördagar: {value}', + 'mobilityPlatform.content.parkingChargeZones.price.sunday': 'Avgiftsbelagd söndagar: {value}', 'mobilityPlatform.content.description.notAvailable': 'Beskrivningstext är inte tillgänglig.', 'mobilityPlatform.content.cityBikes.title': 'Stadscykelstation', 'mobilityPlatform.content.cityBikes.name': 'Station: {value}', @@ -903,17 +921,31 @@ const translations = { 'mobilityPlatform.content.publicParking.totalTime': 'Parkeringstidsgräns: {value} timmar', 'mobilityPlatform.content.parkingMachine.title': 'Parkeringsautomat', 'mobilityPlatform.content.parkingMachine.location': 'Plats: {value}', - 'mobilityPlatform.content.parkingMachine.payment': 'För en avgift: {value} €/h', + 'mobilityPlatform.content.parkingMachine.payment': 'För en avgift: {value}', 'mobilityPlatform.content.parkingMachine.paymentTypes': 'Betalningstyper: {value}', 'mobilityPlatform.content.parkingMachine.otherInfo': 'Ytterligare info: {value}', 'mobilityPlatform.content.crosswalks.title': 'Övergångställe', 'mobilityPlatform.content.railways.train': 'Tåg: {value1} {value2}', - 'mobilityPlatform.content.departingTrains.title': 'Avgående tåg', - 'mobilityPlatform.content.arrivingTrains.title': 'Inkommande tåg', + 'mobilityPlatform.content.departingTrains.title': 'Avgående tåg:', + 'mobilityPlatform.content.arrivingTrains.title': 'Inkommande tåg:', 'mobilityPlatform.content.departingTrains.empty': 'Inga avgående tåg', 'mobilityPlatform.content.arrivingTrains.empty': 'Inga inkommande tåg', 'mobilityPlatform.parkAndRide.content.subtitle': 'Infartspark for cyklarna', 'mobilityPlatform.content.barbecuePlace.title': 'Grill- och eldningplats', + 'mobilityPlatform.content.barbecuePlace.manufacturer': 'Tillverkare: {value}', + 'mobilityPlatform.content.barbecuePlace.model': 'Modell: {value}', + 'mobilityPlatform.content.airport.title': 'Åbo flygplats', + 'mobilityPlatform.content.airport.arrivals': 'Ankommande flygningar:', + 'mobilityPlatform.content.airport.departees': 'Avgående flygningar:', + 'mobilityPlatform.content.airport.departees.empty': 'Inga avgående flygningar.', + 'mobilityPlatform.content.airport.arrivals.empty': 'Inga inkommande flygningar.', + 'mobilityPlatform.content.airport.cities.turku': 'Till Åbo', + 'mobilityPlatform.content.airport.cities.stockholm': 'Till Stockholm', + 'mobilityPlatform.content.airport.cities.mariehamn': 'Till Mariehamn', + 'mobilityPlatform.content.airport.cities.riga': 'Till Riga', + 'mobilityPlatform.content.airport.cities.gdansk': 'Till Gdańsk', + 'mobilityPlatform.content.airport.cities.rome': 'Till Rom', + 'mobilityPlatform.content.airport.cities.rhodes': 'Till Rhodos', // Info text 'mobilityPlatform.info.description.title': 'Beskrivning av rutten', @@ -975,6 +1007,7 @@ const translations = { 'mobilityPlatform.info.airMonitoring.link': 'För mer information besök: https://sv.ilmatieteenlaitos.fi/luftkvalitet', 'mobilityPlatform.info.parkAndRideBicycles': 'Infartsparkeringen erbjuder möjlighet att lämna din egen cykel på parkeringsplatsen och fortsätta resan med buss. Inom Föli-området finns flera infartsparkeringsplatser för cyklar. Infartsparkeringen är gratis och endast avsedd för dem som fortsätter sin resa med kollektivtrafik.', 'mobilityPlatform.info.barbecuePlaces': 'Kartan visar eldnings- och grillplatser i Åbo. Det är tillåtet att göra upp öppen eld endast vid särskilt avsedda eldningsplatser. Att göra upp eld på andra platser är förbjudet.', + 'mobilityPlatform.info.airport': 'Kartan visar Åbo flygplats och flygtidtabeller för den aktuella dagen. Flygtrafiken är uppdelad i flyg som avgår från och ankommer till flygplatsen. Uppgifterna kommer från Finavias gränssnitt.', // Bicycle routes 'mobilityPlatform.menu.bicycleRoutes.euroVelo': 'EuroVelo 10 är en europeisk cykelrutt som följer den finländska kusten. Sträckan mellan Helsingfors och Åbo är skyltad.', diff --git a/src/redux/actions/search.js b/src/redux/actions/search.js index bba5a0899..f41f2d82c 100644 --- a/src/redux/actions/search.js +++ b/src/redux/actions/search.js @@ -4,11 +4,11 @@ import ServiceMapAPI from '../../utils/newFetch/ServiceMapAPI'; import { getLocaleString } from '../selectors/locale'; import { searchResults } from './fetchDataActions'; import { isEmbed } from '../../utils/path'; +import config from '../../../config'; // Actions const { isFetching, fetchSuccess, fetchProgressUpdateConcurrent } = searchResults; - const smFetch = (dispatch, options) => { let results = []; const smAPI = new ServiceMapAPI(); @@ -51,10 +51,14 @@ const smFetch = (dispatch, options) => { return results; }; +const getCities = citiesObj => Object.keys(citiesObj).filter(key => citiesObj[key] === true); const fetchSearchResults = (options = null) => async (dispatch, getState) => { const searchFetchState = getState().searchResults; const { locale } = getState().user; + const { cities } = getState().settings; + const citySettings = getCities(cities); + const municipalities = citySettings?.length ? citySettings?.join(',') : config.cities; const searchQuery = options.q || options.address @@ -79,6 +83,7 @@ const fetchSearchResults = (options = null) => async (dispatch, getState) => { ]; const fetchOptions = { ...options, + municipality: municipalities, language: locale, include: isEmbed() ? extraFields : null, }; @@ -93,7 +98,7 @@ const fetchSearchResults = (options = null) => async (dispatch, getState) => { } // Handle unit results that have no object_type if (options.service_node || options.service_id || options.id || options.level) { - results.forEach((item) => { + results.forEach(item => { item.object_type = 'unit'; }); } @@ -102,7 +107,7 @@ const fetchSearchResults = (options = null) => async (dispatch, getState) => { const isExactAddress = /\d/.test(options.address); // If address has a number specified, we can assume it is exact address instead of street let addressData = results[0]; const unitData = results[1]; - unitData.forEach((unit) => { unit.object_type = 'unit'; }); + unitData.forEach(unit => { unit.object_type = 'unit'; }); if (isExactAddress) { addressData = addressData.filter( res => getLocaleString(locale, res.name) === options.address, @@ -112,7 +117,7 @@ const fetchSearchResults = (options = null) => async (dispatch, getState) => { } // Handle event search results if (options.events) { - results.forEach((event) => { + results.forEach(event => { event.object_type = 'event'; const eventUnit = event.location; if (eventUnit) { diff --git a/src/utils/newFetch/ServiceMapAPI.js b/src/utils/newFetch/ServiceMapAPI.js index 30ff25b4a..9c5e96e30 100644 --- a/src/utils/newFetch/ServiceMapAPI.js +++ b/src/utils/newFetch/ServiceMapAPI.js @@ -36,7 +36,7 @@ export default class ServiceMapAPI extends HttpClient { }; return this.getConcurrent('search', options); - } + }; searchSuggestions = async (query, additionalOptions) => { if (typeof query !== 'string') { @@ -50,7 +50,7 @@ export default class ServiceMapAPI extends HttpClient { }; return this.getSinglePage('search', options); - } + }; serviceNodeSearch = async (idList, additionalOptions) => { if (typeof idList !== 'string') { @@ -67,7 +67,7 @@ export default class ServiceMapAPI extends HttpClient { }; return this.getConcurrent('unit', options); - } + }; serviceUnitSearch = async (serviceId, additionalOptions) => { if (typeof serviceId !== 'string') { @@ -84,7 +84,7 @@ export default class ServiceMapAPI extends HttpClient { }; return this.getConcurrent('unit', options); - } + }; // Fetch units of multiple services concurrently serviceUnits = async (idList, additionalOptions) => { @@ -101,9 +101,9 @@ export default class ServiceMapAPI extends HttpClient { }; return this.getConcurrent('unit', options); - } + }; - serviceNames = async (idList) => { + serviceNames = async idList => { if (typeof idList !== 'string') { throw new APIFetchError('Invalid idList string provided to ServiceMapAPI serviceNames method'); } @@ -113,7 +113,7 @@ export default class ServiceMapAPI extends HttpClient { page_size: '1000', }; return this.get('service_node', options); - } + }; // Fetch list of all services services = async () => { @@ -122,7 +122,7 @@ export default class ServiceMapAPI extends HttpClient { page_size: '500', }; return this.getConcurrent('service', options); - } + }; statisticalGeometry = async () => { const options = { @@ -132,7 +132,7 @@ export default class ServiceMapAPI extends HttpClient { type: 'statistical_district', }; return this.getConcurrent('administrative_division', options); - } + }; areas = async (idList, geometry, additionalOptions) => { if (typeof idList !== 'string') { @@ -146,7 +146,7 @@ export default class ServiceMapAPI extends HttpClient { ...additionalOptions, }; return this.getConcurrent('administrative_division', options); - } + }; areaGeometry = async (id, additionalOptions) => { if (typeof id !== 'string') { @@ -161,9 +161,9 @@ export default class ServiceMapAPI extends HttpClient { ...additionalOptions, }; return this.getConcurrent('administrative_division', options); - } + }; - areaUnits = async (nodeID) => { + areaUnits = async nodeID => { if (typeof nodeID !== 'string') { throw new APIFetchError('Invalid nodeID string provided to ServiceMapAPI area unit fetch method'); } @@ -177,9 +177,9 @@ export default class ServiceMapAPI extends HttpClient { }; return this.getConcurrent('unit', options); - } + }; - parkingAreaInfo = async (params) => { + parkingAreaInfo = async params => { const options = { page: 1, page_size: 1, @@ -189,9 +189,9 @@ export default class ServiceMapAPI extends HttpClient { }; return this.getSinglePage('administrative_division', options); - } + }; - units = async (additionalOptions) => { + units = async additionalOptions => { const options = { page_size: 200, only: 'street_address,location,name,municipality,accessibility_shortcoming_count,service_nodes,contract_type', @@ -201,9 +201,9 @@ export default class ServiceMapAPI extends HttpClient { }; return this.getConcurrent('unit', options); - } + }; - sendStats = async (data) => { + sendStats = async data => { if (typeof data.embed === 'undefined' || typeof data.mobile_device === 'undefined') { throw new APIFetchError('Invalid data provided for ServiceMapAPI sendStats fetch method'); } @@ -217,5 +217,5 @@ export default class ServiceMapAPI extends HttpClient { const baseUrlOverride = config.serviceMapAPI.root; return this.post('stats', data, baseUrlOverride); - } + }; } diff --git a/src/views/AreaView/AreaView.js b/src/views/AreaView/AreaView.js index a5d2f7e1c..685666618 100644 --- a/src/views/AreaView/AreaView.js +++ b/src/views/AreaView/AreaView.js @@ -6,7 +6,7 @@ import { FormattedMessage } from 'react-intl'; import { useDispatch, useSelector } from 'react-redux'; import { List, ListItem, Typography } from '@mui/material'; import { - BusinessCenter, EscalatorWarning, LocationCity, Map, + BusinessCenter, DirectionsWalk, EscalatorWarning, LocationCity, Map, } from '@mui/icons-material'; import { visuallyHidden } from '@mui/utils'; import { focusDistrict, focusDistricts, useMapFocusDisabled } from '../MapView/utils/mapActions'; @@ -31,6 +31,7 @@ import StatisticalDistrictList from './components/StatisticalDistrictList'; import useMobileStatus from '../../utils/isMobile'; import MapUtility from '../../utils/mapUtility'; import config from '../../../config'; +import MobilityResultTab from './components/MobilityResultTab'; const AreaView = ({ setSelectedDistrictType, @@ -59,10 +60,10 @@ const AreaView = ({ const history = useHistory(); const isMobile = useMobileStatus(); const addressDistrict = useSelector(getAddressDistrict); - const selectedDistrictType = useSelector((state) => state.districts.selectedDistrictType); - const districtsFetching = useSelector((state) => state.districts.districtsFetching); + const selectedDistrictType = useSelector(state => state.districts.selectedDistrictType); + const districtsFetching = useSelector(state => state.districts.districtsFetching); const getLocaleText = useLocaleText(); - const openItems = useSelector((state) => state.districts.openItems); + const openItems = useSelector(state => state.districts.openItems); const selectedDistrictGeometry = selectedDistrictData[0]?.boundary; const searchParams = parseSearchParams(location.search); @@ -76,8 +77,8 @@ const AreaView = ({ const getInitialOpenItems = () => { if (selectedAreaType) { const category = dataStructure.find( - (data) => data.id === selectedAreaType - || data.districts.some((obj) => obj.id === selectedAreaType), + data => data.id === selectedAreaType + || data.districts.some(obj => obj.id === selectedAreaType), ); return [category?.id]; } return openItems; @@ -89,7 +90,7 @@ const AreaView = ({ // Pending request to focus map to districts. Executed once district data is loaded const [focusTo, setFocusTo] = useState(null); - const focusMapToDistrict = (district) => { + const focusMapToDistrict = district => { if (!mapFocusDisabled && map && district?.boundary) { focusDistrict(map, district.boundary.coordinates); } @@ -101,12 +102,12 @@ const AreaView = ({ lon: `${selectedAddress.location.coordinates[0]}`, page: 1, page_size: 200, - type: `${dataStructure.map((item) => item.districts.map((obj) => obj.id)).join(',')}`, + type: `${dataStructure.map(item => item.districts.map(obj => obj.id)).join(',')}`, geometry: true, unit_include: 'name,location', }; await districtFetch(options) - .then((data) => { + .then(data => { setDistrictAddressData({ address: selectedAddress, districts: data.results, @@ -152,10 +153,10 @@ const AreaView = ({ }, [selectedAddress]); useEffect(() => { - selectedSubdistricts.forEach((district) => { + selectedSubdistricts.forEach(district => { // Fetch geographical districts unless currently fetching or already fetched if (!unitsFetching.includes(district) - && !subdistrictUnits.some((unit) => unit.division_id === district)) { + && !subdistrictUnits.some(unit => unit.division_id === district)) { fetchDistrictUnitList(district); } }); @@ -172,7 +173,7 @@ const AreaView = ({ } else if (focusTo === 'subdistricts') { if (selectedDistrictGeometry) { const filtetedDistricts = selectedDistrictData.filter( - (i) => selectedSubdistricts.includes(i.ocd_id), + i => selectedSubdistricts.includes(i.ocd_id), ); setFocusTo(null); focusDistricts(map, filtetedDistricts); @@ -209,11 +210,11 @@ const AreaView = ({ } // Fetch and select area from url parameters - if (selectedArea && !dataStructure.some((obj) => obj.id === selectedArea)) { + if (selectedArea && !dataStructure.some(obj => obj.id === selectedArea)) { fetchDistricts(selectedAreaType); if (!embed) { const category = dataStructure.find( - (data) => data.districts.some((obj) => obj.id === selectedAreaType), + data => data.districts.some(obj => obj.id === selectedAreaType), ); dispatch(handleOpenItems(category.id)); } else { @@ -228,7 +229,7 @@ const AreaView = ({ if (searchParams.parkingSpaces) { const parkingAreas = searchParams.parkingSpaces.split(','); setSelectedParkingAreas(parkingAreas); - parkingAreas.forEach((area) => { + parkingAreas.forEach(area => { dispatch(fetchParkingAreaGeometry(area)); }); } @@ -245,14 +246,14 @@ const AreaView = ({ // Set selected geographical services from url parameters if (searchParams.services) { const services = searchParams.services.split(','); - const convertedServices = services.map((service) => parseInt(service, 10)); + const convertedServices = services.map(service => parseInt(service, 10)); setSelectedDistrictServices(convertedServices); } // Set address from url paramters if (searchParams.lat && searchParams.lng) { fetchAddress({ lat: searchParams.lat, lng: searchParams.lng }) - .then((data) => setSelectedAddress(data)); + .then(data => setSelectedAddress(data)); } } else if (!districtData.length) { // Arriving to page first time, without url parameters fetchDistricts(); @@ -282,6 +283,10 @@ const AreaView = ({ /> ); + const renderMobilityResultTab = () => ( + + ); + const areaSectionSelection = (open, i) => { setAreaSelection(!open ? i : null); if (selectedDistrictType) { @@ -308,7 +313,7 @@ const AreaView = ({ const externalTheme = config.themePKG; const isExternalTheme = !externalTheme || externalTheme === 'undefined' ? null : externalTheme; - const filterCategories = (data) => data.reduce((acc, curr) => { + const filterCategories = data => data.reduce((acc, curr) => { if (curr.type !== 'statistic') { acc.push(curr); } @@ -329,6 +334,12 @@ const AreaView = ({ icon: , type: 'geographic', }, + { + component: renderMobilityResultTab(), + title: intl.formatMessage({ id: 'area.tab.mobilityTest.results' }), + icon: , + type: 'mobilityResults', + }, { component: , title: intl.formatMessage({ id: 'area.tab.statisticalDistricts' }), diff --git a/src/views/AreaView/components/GeographicalTab/GeographicalTab.js b/src/views/AreaView/components/GeographicalTab/GeographicalTab.js index 2fc7eaf8a..f9a7e6788 100644 --- a/src/views/AreaView/components/GeographicalTab/GeographicalTab.js +++ b/src/views/AreaView/components/GeographicalTab/GeographicalTab.js @@ -43,8 +43,7 @@ const GeographicalTab = ({ useSelector(state => state.districts.openItems).find(item => geographicalDistricts.includes(item)) || [], ); - - const setRadioButtonValue = (district) => { + const setRadioButtonValue = district => { if (!district.data.some(obj => obj.boundary)) { dispatch(fetchDistrictGeometry(district.name)); } @@ -58,7 +57,7 @@ const GeographicalTab = ({ } }; - const handleCategoryOpen = (id) => { + const handleCategoryOpen = id => { setOpenCategory(id); dispatch(handleOpenGeographicalCategory(id)); }; @@ -93,7 +92,6 @@ const GeographicalTab = ({ } }, [selectedDistrictType]); - const renderAddressInfo = useCallback(() => { const localPostArea = localAddressData.districts.find(obj => obj.type === 'postcode_area'); const localNeighborhood = localAddressData.districts.find(obj => obj.type === 'neighborhood'); @@ -118,7 +116,6 @@ const GeographicalTab = ({ ); }, [localAddressData]); - const render = () => { const districtItems = districtData.filter(obj => geographicalDistricts.includes(obj.id)); return ( @@ -130,7 +127,7 @@ const GeographicalTab = ({ - {districtItems.map((district) => { + {districtItems.map(district => { const opened = openCategory === district.id; const selected = selectedDistrictType === district.id; return ( diff --git a/src/views/AreaView/components/MobilityResultTab/MobilityResultTab.js b/src/views/AreaView/components/MobilityResultTab/MobilityResultTab.js new file mode 100644 index 000000000..22d2209e0 --- /dev/null +++ b/src/views/AreaView/components/MobilityResultTab/MobilityResultTab.js @@ -0,0 +1,52 @@ +import React from 'react'; +import { List, ListItem } from '@mui/material'; +import styled from '@emotion/styled'; +import { useMobilityPlatformContext } from '../../../../context/MobilityPlatformContext'; +import MobilityToggleButton from '../../../MobilitySettingsView/components/MobilityToggleButton'; +import InfoTextBox from '../../../../components/MobilityPlatform/InfoTextBox/InfoTextBox'; + +const MobilityResultTab = () => { + const { showMobilityResults, setShowMobilityResults } = useMobilityPlatformContext(); + + const toggleMobilityResults = () => { + setShowMobilityResults(current => !current); + }; + + return ( +
    + + + + + + + + + + + + + + +
    + ); +}; + +const StyledContainer = styled.div(({ theme }) => ({ + padding: `0 ${theme.spacing(1)}`, +})); + +const StyledMargin = styled.div(({ theme }) => ({ + marginBottom: theme.spacing(1.5), +})); + +export default MobilityResultTab; diff --git a/src/views/AreaView/components/MobilityResultTab/index.js b/src/views/AreaView/components/MobilityResultTab/index.js new file mode 100644 index 000000000..97e00c2db --- /dev/null +++ b/src/views/AreaView/components/MobilityResultTab/index.js @@ -0,0 +1,3 @@ +import MobilityResultTab from './MobilityResultTab'; + +export default MobilityResultTab; diff --git a/src/views/AreaView/components/StatisticalDistrictList/StatisticalDistrictListComponent.js b/src/views/AreaView/components/StatisticalDistrictList/StatisticalDistrictListComponent.js index 83509e3f0..1926573ef 100644 --- a/src/views/AreaView/components/StatisticalDistrictList/StatisticalDistrictListComponent.js +++ b/src/views/AreaView/components/StatisticalDistrictList/StatisticalDistrictListComponent.js @@ -33,7 +33,6 @@ import { import StatisticalDistrictUnitList from '../StatisticalDistrictUnitList'; import StatisticalDistrictListContent from './StatisticalDistrictListContent'; - const StatisticalDistrictListComponent = ({ classes, }) => { @@ -85,12 +84,12 @@ const StatisticalDistrictListComponent = ({ } }; - const renderLayers = (category) => { + const renderLayers = category => { let component = null; if (category?.layers) { const isForecast = category?.type === 'forecast'; component = ( - category.layers.map((layer) => { + category.layers.map(layer => { const selected = section === layer; const serviceTitle = formatMessage({ id: 'area.statisticalDistrict.service.filter' }); const disableServicesAccordion = !Object.keys(selectedAreas).some(a => selectedAreas[a]); @@ -174,7 +173,7 @@ const StatisticalDistrictListComponent = ({ return ( { - layerCategoryKeys.map((key) => { + layerCategoryKeys.map(key => { const layerCategory = layerCategories[key]; const selected = layerCategory.type === selectedCategory; const titleText = `${formatMessage({ id: `area.list.statistic.${layerCategory.type}` })} ${layerCategory.year}`; @@ -215,24 +214,21 @@ const StatisticalDistrictListComponent = ({ }; return ( - <> - { isFetchingDistricts - ? ( - - - - ) : renderLayerCategories() - } - + isFetchingDistricts + ? ( + + + + ) : renderLayerCategories() ); }; StatisticalDistrictListComponent.propTypes = { - classes: PropTypes.objectOf(PropTypes.any).isRequired, + classes: PropTypes.objectOf(PropTypes.string).isRequired, }; export default StatisticalDistrictListComponent; diff --git a/src/views/AreaView/components/StatisticalDistrictUnitList/StatisticalDistrictUnitListComponent.js b/src/views/AreaView/components/StatisticalDistrictUnitList/StatisticalDistrictUnitListComponent.js index 01eb007b2..1c440cfb5 100644 --- a/src/views/AreaView/components/StatisticalDistrictUnitList/StatisticalDistrictUnitListComponent.js +++ b/src/views/AreaView/components/StatisticalDistrictUnitList/StatisticalDistrictUnitListComponent.js @@ -27,14 +27,13 @@ import { getServiceFilteredStatisticalDistrictUnits, } from '../../../../redux/selectors/statisticalDistrict'; - // Custom uncontrolled checkbox that allows default value const UnitCheckbox = ({ handleUnitCheckboxChange, id, defaultChecked, classes, inputProps, }) => { const [checked, setChecked] = useState(defaultChecked); - const handleChange = (e) => { + const handleChange = e => { setChecked(!checked); handleUnitCheckboxChange(e, id); }; @@ -50,7 +49,6 @@ const UnitCheckbox = ({ ); }; - const StatisticalDistrictUnitListComponent = ({ classes, handleUnitCheckboxChange, @@ -64,7 +62,7 @@ const StatisticalDistrictUnitListComponent = ({ const [initialCheckedItems] = useState(selectedServices || []); const [filterValue, setFilterValue] = useState(''); const services = useSelector(getOrderedStatisticalDistrictServices); - const filteredServiceList = services.filter((category) => { + const filteredServiceList = services.filter(category => { if (filterValue === '') return true; if (selectedServices[category.id]) return true; return getLocaleText(category.name).includes(filterValue); @@ -137,7 +135,7 @@ const StatisticalDistrictUnitListComponent = ({ } - {filteredServiceList.map((service) => { + {filteredServiceList.map(service => { const units = statisticalDistrictUnits .filter(u => u?.services?.some(s => s.id === service.id)); const disableUnitAccordion = !selectedServices[service.id] || units.length === 0; @@ -213,7 +211,6 @@ UnitCheckbox.defaultProps = { export default StatisticalDistrictUnitListComponent; - const StyledRowContainer = styled('div')` display: flex; flex-direction: row; diff --git a/src/views/AreaView/components/styled/styled.js b/src/views/AreaView/components/styled/styled.js new file mode 100644 index 000000000..3994cbcd4 --- /dev/null +++ b/src/views/AreaView/components/styled/styled.js @@ -0,0 +1,137 @@ +import styled from '@emotion/styled'; +import { + Divider, List, ListItem, Typography, +} from '@mui/material'; +import { SMAccordion } from '../../../../components'; + +/** + * Common styled components for AreaView + */ +const levelTwo = () => ({ + backgroundColor: 'rgb(250,250,250)', +}); + +const levelThree = () => ({ + backgroundColor: 'rgb(245,245,245)', +}); + +const levelFour = () => ({ + backgroundColor: 'rgb(240,240,240)', +}); + +const StyledDivider = styled(Divider)(() => ({ + marginLeft: -72, +})); + +const StyledDistrictServiceList = styled('div')(() => ({ + boxShadow: 'inset 0px 4px 4px rgba(0, 0, 0, 0.06)', +})); + +const StyledDistrictServiceListLevelFour = styled(StyledDistrictServiceList)(() => levelFour()); +const StyledDistrictServiceListLevelThree = styled(StyledDistrictServiceList)(() => levelThree()); +const StyledListLevelThree = styled(List)(() => levelThree()); + +const StyledServiceList = styled('div')(({ theme }) => ({ + paddingTop: theme.spacing(2), + paddingBottom: theme.spacing(2), + paddingLeft: 77, + backgroundColor: 'rgb(230,243,254)', +})); + +const StyledServiceTabServiceList = styled('div')(({ theme }) => ({ + paddingLeft: theme.spacing(10), + paddingTop: theme.spacing(2), + paddingBottom: theme.spacing(2), +})); + +const StyledListItem = styled(ListItem)(({ theme }) => ({ + padding: 0, + minHeight: theme.spacing(7), +})); + +const StyledAreaListItem = styled(StyledListItem)(({ theme }) => ({ + paddingLeft: theme.spacing(8), + paddingRight: theme.spacing(2), +})); + +const StyledListNoPadding = styled(List)(({ theme }) => ({ + padding: 0, + marginTop: theme.spacing(0), + marginBottom: theme.spacing(0), + '& li:last-of-type': { + borderBottom: 'none', + }, +})); + +const StyledListNoPaddingLevelTwo = styled(StyledListNoPadding)(() => levelTwo()); + +const StyledListNoPaddingLevelThree = styled(StyledListNoPadding)(() => levelThree()); + +const StyledUnitsAccordion = styled(SMAccordion)(() => ({ + height: 48, + backgroundColor: 'rgba(222, 222, 222, 0.56)', +})); + +const StyledCaptionText = styled(Typography)(() => ({ + color: '#000', + fontWeight: 'normal', +})); + +const StyledLoadingText = styled('div')(() => ({ + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + height: 56, +})); + +const StyledBoldText = styled(Typography)(() => ({ + fontWeight: 'bold', +})); + +const StyledCheckBoxIcon = styled('span')(() => ({ + margin: 2, + width: 18, + height: 18, + backgroundColor: '#fff', + border: '0.5px solid #949494', + boxShadow: 'inset 1px 1px 2px rgba(0, 0, 0, 0.05)', + borderRadius: 2, +})); + +const StyledUnitListArea = styled('div')(() => ({ + textAlign: 'left', + backgroundColor: 'rgba(222, 222, 222, 0.56)', +})); + +const StyledAccordionServiceTitle = styled(SMAccordion)(({ theme }) => ({ + paddingLeft: theme.spacing(10), +})); + +const StyledUnitList = styled(List)(({ theme }) => ({ + paddingLeft: theme.spacing(10), + paddingRight: theme.spacing(4), + paddingBottom: theme.spacing(1), +})); + +export { + StyledDivider, + StyledDistrictServiceList, + StyledDistrictServiceListLevelThree, + StyledDistrictServiceListLevelFour, + StyledListLevelThree, + StyledListItem, + StyledAreaListItem, + StyledListNoPadding, + StyledListNoPaddingLevelTwo, + StyledListNoPaddingLevelThree, + StyledServiceList, + StyledServiceTabServiceList, + StyledUnitsAccordion, + StyledCaptionText, + StyledLoadingText, + StyledBoldText, + StyledCheckBoxIcon, + StyledUnitListArea, + StyledAccordionServiceTitle, + StyledUnitList, +}; diff --git a/src/views/EmbedderView/EmbedderView.js b/src/views/EmbedderView/EmbedderView.js index c8c57ca36..ebcb62892 100644 --- a/src/views/EmbedderView/EmbedderView.js +++ b/src/views/EmbedderView/EmbedderView.js @@ -12,16 +12,17 @@ import { useSelector } from 'react-redux'; import * as smurl from './utils/url'; import isClient, { uppercaseFirst } from '../../utils'; import { getEmbedURL, getLanguage } from './utils/utils'; -import EmbedController from './components/EmbedController'; -import IFramePreview from './components/IFramePreview'; import paths from '../../../config/paths'; import embedderConfig from './embedderConfig'; import SettingsUtility from '../../utils/settings'; import useLocaleText from '../../utils/useLocaleText'; import { useUserLocale } from '../../utils/user'; +import { useMobilityPlatformContext } from '../../context/MobilityPlatformContext'; +import config from '../../../config'; +import EmbedController from './components/EmbedController'; +import IFramePreview from './components/IFramePreview'; import EmbedHTML from './components/EmbedHTML'; import TopBar from '../../components/TopBar'; -import config from '../../../config'; import { CloseButton, SMButton } from '../../components'; const hideCitiesIn = [ @@ -60,7 +61,7 @@ const EmbedderView = ({ } const cityOption = (search?.city !== '' && search?.city?.split(',')) || citySettings; - const citiesToReduce = cityOption.length > 0 ? cityOption : embedderConfig.CITIES.filter((v) => v); + const citiesToReduce = cityOption.length > 0 ? cityOption : embedderConfig.CITIES.filter(v => v); // If external theme (by Turku) is true, then can be used to select which content to render const externalTheme = config.themePKG; @@ -77,12 +78,15 @@ const EmbedderView = ({ const defaultFixedHeight = embedderConfig.DEFAULT_CUSTOM_WIDTH; const iframeConfig = embedderConfig.DEFAULT_IFRAME_PROPERTIES || {}; const defaultService = 'none'; - const page = useSelector((state) => state.user.page); - const selectedUnit = useSelector((state) => state.selectedUnit.unit.data); - const currentService = useSelector((state) => state.service.current); + const page = useSelector(state => state.user.page); + const selectedUnit = useSelector(state => state.selectedUnit.unit.data); + const currentService = useSelector(state => state.service.current); const getLocaleText = useLocaleText(); const userLocale = useUserLocale(); + const { showAccessibilityAreas, accessibilityAreasData } = useMobilityPlatformContext(); + const hasAccessibilityAreas = accessibilityAreasData.some(item => item?.extra?.kohde_ID === selectedUnit?.id); + // States const [language, setLanguage] = useState(defaultLanguage); const [map, setMap] = useState(defaultMap); @@ -109,6 +113,9 @@ const EmbedderView = ({ const [underPass, setUnderpass] = useState(false); const [overPass, setOverPass] = useState(false); const [publicBenches, setPublicBenches] = useState(false); + const [accessibilityAreas, setAccessibilityAreas] = useState(showAccessibilityAreas.all); + const [accessibilityAreasWalk, setAccessibilityAreasWalk] = useState(showAccessibilityAreas.walking); + const [accessibilityAreasBicycle, setAccessibilityAreasBicycle] = useState(showAccessibilityAreas.cycling); const boundsRef = useRef([]); const dialogRef = useRef(); @@ -136,6 +143,9 @@ const EmbedderView = ({ underPass, overPass, publicBenches, + accessibilityAreas, + accessibilityAreasWalk, + accessibilityAreasBicycle, bbox: selectedBbox, }); @@ -171,7 +181,7 @@ const EmbedderView = ({ buttons[0].focus(); }; - const setBoundsRef = useCallback((bounds) => { + const setBoundsRef = useCallback(bounds => { boundsRef.current = bounds; }, []); @@ -194,7 +204,7 @@ const EmbedderView = ({ }; // Run timeout function and cancel previous if it exists - const runTimeoutFunction = (func) => { + const runTimeoutFunction = func => { if (timeout) { clearTimeout(timeout); } @@ -202,7 +212,7 @@ const EmbedderView = ({ }; // Figure out embed html - const createEmbedHTML = useCallback((url) => { + const createEmbedHTML = useCallback(url => { const showListBottom = showUnitList === 'bottom'; if (!url) { return ''; @@ -247,13 +257,13 @@ const EmbedderView = ({ showUnitList, ]); - const showCities = (embedUrl) => { + const showCities = embedUrl => { if (typeof embedUrl !== 'string') { return false; } const originalUrl = embedUrl.replace('/embed', ''); let show = true; - hideCitiesIn.forEach((r) => { + hideCitiesIn.forEach(r => { if (show) { show = !r.test(originalUrl); } @@ -261,13 +271,13 @@ const EmbedderView = ({ return show; }; - const showServices = (embedUrl) => { + const showServices = embedUrl => { if (typeof embedUrl !== 'string') { return false; } const originalUrl = embedUrl.replace('/embed', ''); let show = true; - hideServicesIn.forEach((r) => { + hideServicesIn.forEach(r => { if (show) { show = !r.test(originalUrl); } @@ -279,8 +289,8 @@ const EmbedderView = ({ * Render language controls */ const renderLanguageControl = () => { - const description = (locale) => intl.formatMessage({ id: `embedder.language.description.${locale}` }); - const languageControls = (generateLabel) => Object.keys(embedderConfig.LANGUAGES).map((lang) => ({ + const description = locale => intl.formatMessage({ id: `embedder.language.description.${locale}` }); + const languageControls = generateLabel => Object.keys(embedderConfig.LANGUAGES).map(lang => ({ value: lang, label: `${uppercaseFirst( embedderConfig.LANGUAGES[userLocale][lang], @@ -306,8 +316,8 @@ const EmbedderView = ({ * Render map controls */ const renderMapTypeControl = () => { - const getLabel = (map) => intl.formatMessage({ id: `settings.map.${map}` }); - const mapControls = (generateLabel) => embedderConfig.BACKGROUND_MAPS.map((map) => ({ + const getLabel = map => intl.formatMessage({ id: `settings.map.${map}` }); + const mapControls = generateLabel => embedderConfig.BACKGROUND_MAPS.map(map => ({ value: map, label: `${generateLabel(map)}`, })); @@ -332,12 +342,12 @@ const EmbedderView = ({ return null; } const cities = city; - const cityControls = embedderConfig.CITIES.filter((v) => v).map((city) => ({ + const cityControls = embedderConfig.CITIES.filter(v => v).map(city => ({ key: city, value: !!cities[city], label: uppercaseFirst(city), icon: null, - onChange: (v) => { + onChange: v => { const newCities = {}; Object.assign(newCities, cities); newCities[city] = v; @@ -362,8 +372,8 @@ const EmbedderView = ({ if (!showServices(embedUrl)) { return null; } - const getLabel = (service) => intl.formatMessage({ id: `embedder.service.${service}` }); - const serviceControls = (generateLabel) => ['none', 'common', 'all'].map((service) => ({ + const getLabel = service => intl.formatMessage({ id: `embedder.service.${service}` }); + const serviceControls = generateLabel => ['none', 'common', 'all'].map(service => ({ value: service, label: generateLabel(service), })); @@ -491,17 +501,40 @@ const EmbedderView = ({ { key: 'units', value: showUnits, - onChange: (v) => setShowUnits(v), + onChange: v => setShowUnits(v), icon: null, labelId: 'embedder.options.label.units', }, + { + key: 'accessibilityAreas', + value: accessibilityAreas, + onChange: v => setAccessibilityAreas(v), + icon: null, + labelId: 'embedder.options.label.units.accessibilityAreas', + }, + { + key: 'accessibilityAreasWalk', + value: accessibilityAreasWalk, + onChange: v => setAccessibilityAreasWalk(v), + icon: null, + labelId: 'embedder.options.label.units.accessibilityAreas.walk', + }, + { + key: 'accessibilityAreasBicycle', + value: accessibilityAreasBicycle, + onChange: v => setAccessibilityAreasBicycle(v), + icon: null, + labelId: 'embedder.options.label.units.accessibilityAreas.bicycle', + }, ]; + const controlsBasic = controls.filter(item => item.key === 'units'); + return ( ); @@ -517,77 +550,77 @@ const EmbedderView = ({ { key: 'bicycleStands', value: bicycleStands, - onChange: (v) => setBicycleStands(v), + onChange: v => setBicycleStands(v), icon: null, labelId: 'mobilityPlatform.menu.showBicycleStands', }, { key: 'frameLockable', value: frameLockable, - onChange: (v) => setFrameLockable(v), + onChange: v => setFrameLockable(v), icon: null, labelId: 'mobilityPlatform.menu.show.hullLockableStands', }, { key: 'cityBikes', value: cityBikes, - onChange: (v) => setCityBikes(v), + onChange: v => setCityBikes(v), icon: null, labelId: 'mobilityPlatform.menu.showCityBikes', }, { key: 'cargoBikes', value: cargoBikes, - onChange: (v) => setCargoBikes(v), + onChange: v => setCargoBikes(v), icon: null, labelId: 'mobilityPlatform.menu.show.cargoBikes', }, { key: 'transit', value: transit, - onChange: (v) => setTransit(v), + onChange: v => setTransit(v), icon: null, labelId: 'embedder.options.label.transit', }, { key: 'crossWalks', value: crossWalks, - onChange: (v) => setCrossWalks(v), + onChange: v => setCrossWalks(v), icon: null, labelId: 'mobilityPlatform.embedded.label.crossWalks', }, { key: 'publicBenches', value: publicBenches, - onChange: (v) => setPublicBenches(v), + onChange: v => setPublicBenches(v), icon: null, labelId: 'mobilityPlatform.embedded.label.publicBenches', }, { key: 'underPass', value: underPass, - onChange: (v) => setUnderpass(v), + onChange: v => setUnderpass(v), icon: null, labelId: 'mobilityPlatform.menu.show.underPasses', }, { key: 'overPass', value: overPass, - onChange: (v) => setOverPass(v), + onChange: v => setOverPass(v), icon: null, labelId: 'mobilityPlatform.menu.show.overPasses', }, { key: 'chargingStation', value: chargingStation, - onChange: (v) => setChargingStation(v), + onChange: v => setChargingStation(v), icon: null, labelId: 'mobilityPlatform.menu.showChargingStations', }, { key: 'rentalCars', value: rentalCars, - onChange: (v) => setRentalCars(v), + onChange: v => setRentalCars(v), icon: null, labelId: 'mobilityPlatform.menu.showRentalCars', }, diff --git a/src/views/EmbedderView/components/EmbedController.js b/src/views/EmbedderView/components/EmbedController.js index a671d5cf3..94618833e 100644 --- a/src/views/EmbedderView/components/EmbedController.js +++ b/src/views/EmbedderView/components/EmbedController.js @@ -48,7 +48,7 @@ const CustomInput = withStyles(customStyles)(({ setValue(initialValue); }, [initialValue]); - const inputOnChange = (e) => { + const inputOnChange = e => { setValue(e.target.value); if (onChange) { onChange(e, e.target.value); @@ -81,7 +81,7 @@ const CustomInput = withStyles(customStyles)(({ aria-label={buttonText} color="primary" className={classes.iconButton} - onClick={(e) => { buttonClick(e, value); }} + onClick={e => { buttonClick(e, value); }} variant="contained" > {buttonText} @@ -121,10 +121,9 @@ const EmbedController = ({ return null; } return ( - <> - - - { + + + { checkboxControls.map(item => ( )) } - - - + + ); }; diff --git a/src/views/EmbedderView/utils/utils.js b/src/views/EmbedderView/utils/utils.js index d165fd378..bf3e55d62 100644 --- a/src/views/EmbedderView/utils/utils.js +++ b/src/views/EmbedderView/utils/utils.js @@ -70,6 +70,15 @@ export const getEmbedURL = (url, params = {}) => { if (params.overPass) { data.overpass = params.overPass ? 1 : 0; } + if (params.accessibilityAreas) { + data.accessibility_areas = params.accessibilityAreas ? 1 : 0; + } + if (params.accessibilityAreasWalk) { + data.accessibility_areas_walk = params.accessibilityAreasWalk ? 1 : 0; + } + if (params.accessibilityAreasBicycle) { + data.accessibility_areas_bicycle = params.accessibilityAreasBicycle ? 1 : 0; + } if (params.bbox) { data.bbox = params.bbox; } diff --git a/src/views/MapView/utils/updateObject.js b/src/views/MapView/utils/updateObject.js new file mode 100644 index 000000000..9f87083c9 --- /dev/null +++ b/src/views/MapView/utils/updateObject.js @@ -0,0 +1,14 @@ +/** + * General function to update object values into state + * @param {*string} key + * @param {*Object} state + * @param {*function} setState + */ +const toggleObjectValue = (key, state, setState) => { + setState(prevState => ({ + ...prevState, + [key]: !prevState[key], + })); +}; + +export default toggleObjectValue; diff --git a/src/views/MobilityPlatformMapView/MobilityPlatformMapView.js b/src/views/MobilityPlatformMapView/MobilityPlatformMapView.js index d445d8948..ddcb83500 100644 --- a/src/views/MobilityPlatformMapView/MobilityPlatformMapView.js +++ b/src/views/MobilityPlatformMapView/MobilityPlatformMapView.js @@ -39,6 +39,9 @@ import RailwayStations from '../../components/MobilityPlatform/RailwayStations'; import AirMonitoring from '../../components/MobilityPlatform/EnvironmentObservations/AirMonitoring'; import ParkAndRideBikes from '../../components/MobilityPlatform/ParkAndRideStops/ParkAndRideBikes'; import BarbecuePlaces from '../../components/MobilityPlatform/BarbecuePlaces'; +import AirportFlights from '../../components/MobilityPlatform/AirportFlights'; +import MobilityProfiles from '../../components/MobilityPlatform/MobilityProfiles'; +import AccessibilityAreas from '../../components/MobilityPlatform/AccessibilityAreas'; const MobilityPlatformMapView = ({ mapObject }) => ( <> @@ -81,6 +84,9 @@ const MobilityPlatformMapView = ({ mapObject }) => ( + + + ); diff --git a/src/views/MobilitySettingsView/MobilitySettingsView.js b/src/views/MobilitySettingsView/MobilitySettingsView.js index 1965cc769..65c4a8d4e 100644 --- a/src/views/MobilitySettingsView/MobilitySettingsView.js +++ b/src/views/MobilitySettingsView/MobilitySettingsView.js @@ -1,15 +1,17 @@ -import { - Checkbox, FormControlLabel, Typography, List, ListItem, -} from '@mui/material'; +import { Typography, List, ListItem } from '@mui/material'; import PropTypes from 'prop-types'; import React, { - useEffect, useMemo, useRef, useState, + useEffect, useRef, useState, } from 'react'; import { Helmet } from 'react-helmet'; import { ReactSVG } from 'react-svg'; import { useSelector } from 'react-redux'; import { useLocation } from 'react-router-dom'; import { Air, WarningAmber } from '@mui/icons-material'; +import { useTheme } from '@mui/styles'; +import { useIntl } from 'react-intl'; +import { css } from '@emotion/css'; +import styled from '@emotion/styled'; import iconBicycle from 'servicemap-ui-turku/assets/icons/icons-icon_bicycle.svg'; import iconBoat from 'servicemap-ui-turku/assets/icons/icons-icon_boating.svg'; import iconCar from 'servicemap-ui-turku/assets/icons/icons-icon_car.svg'; @@ -18,14 +20,13 @@ import iconSnowplow from 'servicemap-ui-turku/assets/icons/icons-icon_street_mai import iconWalk from 'servicemap-ui-turku/assets/icons/icons-icon_walk.svg'; import iconPublicTransport from 'servicemap-ui-turku/assets/icons/icons-icon_public_transport.svg'; import InfoTextBox from '../../components/MobilityPlatform/InfoTextBox'; -import { - fetchBicycleRouteNames, - fetchCultureRouteNames, - fetchMobilityMapData, -} from '../../components/MobilityPlatform/mobilityPlatformRequests/mobilityPlatformRequests'; import useMobilityDataFetch from '../../components/MobilityPlatform/utils/useMobilityDataFetch'; import useLocaleText from '../../utils/useLocaleText'; import { useMobilityPlatformContext } from '../../context/MobilityPlatformContext'; +import useCultureRouteFetch from './hooks/useCultureRouteFetch'; +import useBicycleRouteFetch from './hooks/useBicycleRouteFetch'; +import useSpeedLimitZoneFetch from './hooks/useSpeedLimitZoneFetch'; +import useParkingChargeZoneFetch from './hooks/useParkingChargeZoneFetch'; import TitleBar from '../../components/TitleBar'; import CityBikeInfo from './components/CityBikeInfo'; import EmptyRouteList from './components/EmptyRouteList'; @@ -36,10 +37,11 @@ import ScooterProviderList from './components/ScooterProviderList'; import SMAccordion from '../../components/SMAccordion'; import SpeedLimitZonesList from './components/SpeedLimitZonesList'; import RouteList from './components/RouteList'; +import StreetMaintenanceList from './components/StreetMaintenanceList'; import MobilityToggleButton from './components/MobilityToggleButton'; import AirMonitoringInfo from './components/AirMonitoringInfo'; -const MobilitySettingsView = ({ classes, intl, navigator }) => { +const MobilitySettingsView = ({ navigator }) => { const [pageTitle, setPageTitle] = useState(null); const [openWalkSettings, setOpenWalkSettings] = useState(false); const [openBicycleSettings, setOpenBicycleSettings] = useState(false); @@ -51,9 +53,6 @@ const MobilitySettingsView = ({ classes, intl, navigator }) => { const [openAirMonitoringSettings, setOpenAirMonitoringSettings] = useState(false); const [openRoadworkSettings, setOpenRoadworkSettings] = useState(false); const [openCultureRouteList, setOpenCultureRouteList] = useState(false); - const [cultureRouteList, setCultureRouteList] = useState([]); - const [localizedCultureRoutes, setLocalizedCultureRoutes] = useState([]); - const [bicycleRouteList, setBicycleRouteList] = useState([]); const [openBicycleRouteList, setOpenBicycleRouteList] = useState(false); const [openSpeedLimitList, setOpenSpeedLimitList] = useState(false); const [openParkingChargeZoneList, setOpenParkingChargeZoneList] = useState(false); @@ -63,6 +62,9 @@ const MobilitySettingsView = ({ classes, intl, navigator }) => { const [openNatureTrailsList, setOpenNatureTrailsList] = useState(false); const [openFitnessTrailsList, setOpenFitnessTrailsList] = useState(false); + const intl = useIntl(); + const theme = useTheme(); + const { setOpenMobilityPlatform, showTrafficCounter, @@ -87,8 +89,6 @@ const MobilitySettingsView = ({ classes, intl, navigator }) => { setShowChargingStations, showParkingSpaces, setShowParkingSpaces, - parkingChargeZones, - setParkingChargeZones, parkingChargeZoneId, setParkingChargeZoneId, showParkingChargeZones, @@ -109,8 +109,6 @@ const MobilitySettingsView = ({ classes, intl, navigator }) => { setShowSpeedLimitZones, speedLimitSelections, setSpeedLimitSelections, - speedLimitZones, - setSpeedLimitZones, showPublicToilets, setShowPublicToilets, showScooterNoParking, @@ -174,12 +172,21 @@ const MobilitySettingsView = ({ classes, intl, navigator }) => { setShowParkAndRideBikes, showBarbecuePlaces, setShowBarbecuePlaces, + showAirports, + setShowAirports, } = useMobilityPlatformContext(); const locale = useSelector(state => state.user.locale); const location = useLocation(); const getLocaleText = useLocaleText(); + const iconClass = css({ + fill: 'rgba(0, 0, 0, 255)', + width: '40px', + height: '40px', + marginRight: theme.spacing(1), + }); + const bikeInfo = { paragraph1: 'mobilityPlatform.info.cityBikes.paragraph.1', paragraph2: 'mobilityPlatform.info.cityBikes.paragraph.2', @@ -245,44 +252,10 @@ const MobilitySettingsView = ({ classes, intl, navigator }) => { setOpenMobilityPlatform(true); }, [setOpenMobilityPlatform]); - /** - * Fetch list of routes - * @param {('react').SetStateAction} - * @returns {Array} and sets it into state - */ - useEffect(() => { - if (openWalkSettings) { - fetchCultureRouteNames(setCultureRouteList); - } - }, [openWalkSettings]); - - useEffect(() => { - if (openBicycleSettings) { - fetchBicycleRouteNames(setBicycleRouteList); - } - }, [openBicycleSettings]); - - useEffect(() => { - const options = { - type_name: 'SpeedLimitZone', - page_size: 1000, - latlon: true, - }; - if (openCarSettings) { - fetchMobilityMapData(options, setSpeedLimitZones); - } - }, [openCarSettings, setSpeedLimitZones]); - - useEffect(() => { - const options = { - type_name: 'PaymentZone', - page_size: 10, - latlon: true, - }; - if (openCarSettings) { - fetchMobilityMapData(options, setParkingChargeZones); - } - }, [openCarSettings, setParkingChargeZones]); + const { sortedCultureRoutes, sortedLocalizedCultureRoutes } = useCultureRouteFetch(openWalkSettings, locale); + const { sortedBicycleRoutes } = useBicycleRouteFetch(openBicycleSettings, locale); + const { speedLimitListAsc } = useSpeedLimitZoneFetch(openCarSettings); + const { parkingChargeZonesSorted } = useParkingChargeZoneFetch(openCarSettings); const optionsPaavoTrails = { type_name: 'PaavonPolku', @@ -378,7 +351,14 @@ const MobilitySettingsView = ({ classes, intl, navigator }) => { checkVisibilityValues(showCityBikes, setOpenBicycleSettings); checkVisibilityValues(showCargoBikes, setOpenBicycleSettings); checkVisibilityValues(showParkAndRideBikes, setOpenBicycleSettings); - }, [showBicycleStands, showHullLockableStands, showBikeServiceStations, showCityBikes, showCargoBikes, showParkAndRideBikes]); + }, [ + showBicycleStands, + showHullLockableStands, + showBikeServiceStations, + showCityBikes, + showCargoBikes, + showParkAndRideBikes, + ]); useEffect(() => { checkVisibilityValues(showBicycleRoutes, setOpenBicycleSettings); @@ -468,7 +448,8 @@ const MobilitySettingsView = ({ classes, intl, navigator }) => { useEffect(() => { checkVisibilityValues(showBusStops, setOpenPublicTransportSettings); checkVisibilityValues(showRailwayStations, setOpenPublicTransportSettings); - }, [showBusStops, showRailwayStations]); + checkVisibilityValues(showAirports, setOpenPublicTransportSettings); + }, [showBusStops, showRailwayStations, showAirports]); useEffect(() => { checkVisibilityValues(showAirMonitoringStations, setOpenAirMonitoringSettings); @@ -484,32 +465,6 @@ const MobilitySettingsView = ({ classes, intl, navigator }) => { sv: 'name_sv', }; - /** - * @var {(Array|locale)} - * @function filter array - * @returns {(Array|('react').SetStateAction)} - */ - useEffect(() => { - if (cultureRouteList && cultureRouteList.length > 0) { - setLocalizedCultureRoutes(cultureRouteList.filter(item => item[nameKeys[locale]])); - } - }, [cultureRouteList, locale]); - - /** - * Sort routes in alphapethical order based on current locale. - * If locale is not finnish the filtered list is used. - * @param {Array && locale} - * @function sort - * @returns {Array} - */ - useEffect(() => { - if (cultureRouteList && cultureRouteList.length > 0 && locale === 'fi') { - cultureRouteList.sort((a, b) => a[nameKeys[locale]].localeCompare(b[nameKeys[locale]])); - } else if (localizedCultureRoutes && localizedCultureRoutes.length > 0 && locale !== 'fi') { - localizedCultureRoutes.sort((a, b) => a[nameKeys[locale]].localeCompare(b[nameKeys[locale]])); - } - }, [cultureRouteList, localizedCultureRoutes, locale]); - const sortMarkedTrails = data => { if (data && data.length > 0) { return data.sort((a, b) => a[nameKeys[locale]].split(': ').slice(-1)[0].localeCompare(b[nameKeys[locale]].split(': ').slice(-1)[0])); @@ -519,28 +474,6 @@ const MobilitySettingsView = ({ classes, intl, navigator }) => { const markedTrailsSorted = sortMarkedTrails(markedTrailsList); - /** - * Sort routes in alphapethical order. - * @param {Array && locale} - * @function sort - * @returns {Array} - */ - - useEffect(() => { - const objKeys = { - fi: 'name_fi', - en: 'name_en', - sv: 'name_sv', - }; - - if (bicycleRouteList) { - bicycleRouteList.sort((a, b) => a[objKeys[locale]].localeCompare(b[objKeys[locale]], undefined, { - numeric: true, - sensivity: 'base', - })); - } - }, [bicycleRouteList, locale]); - const sortTrails = data => { if (data && data.length > 0) { return data.sort((a, b) => a.name.localeCompare(b.name)); @@ -845,6 +778,10 @@ const MobilitySettingsView = ({ classes, intl, navigator }) => { setShowRailwayStations(current => !current); }; + const airPortsToggle = () => { + setShowAirports(current => !current); + }; + const roadWorksToggle = () => { setShowRoadworks(current => !current); }; @@ -1373,6 +1310,12 @@ const MobilitySettingsView = ({ classes, intl, navigator }) => { checkedValue: showRailwayStations, onChangeValue: railwayStationsToggle, }, + { + type: 'airPorts', + msgId: 'mobilityPlatform.menu.show.airPorts', + checkedValue: showAirports, + onChangeValue: airPortsToggle, + }, ]; const boatingControlTypes = [ @@ -1471,7 +1414,6 @@ const MobilitySettingsView = ({ classes, intl, navigator }) => { routeAttr={bicycleRouteName} type="BicycleRoute" setRouteState={setBicycleRouteState} - locale={locale} /> ); @@ -1487,16 +1429,15 @@ const MobilitySettingsView = ({ classes, intl, navigator }) => { routeAttr={cultureRouteId} type="CultureRoute" setRouteState={setCultureRouteState} - locale={locale} /> ); const renderSelectTrailText = (visibilityValue, obj, routeList) => { const isObjValid = Object.keys(obj).length > 0; return ( -
    + {visibilityValue && !isObjValid ? : null} -
    + ); }; @@ -1508,82 +1449,28 @@ const MobilitySettingsView = ({ classes, intl, navigator }) => { const renderSettings = (settingVisibility, settingsData) => { if (settingVisibility) { return settingsData.map(item => ( -
    + -
    + )); } return null; }; - // Create array of speed limit values from data and remove duplicates - const speedLimitList = useMemo( - () => [...new Set(speedLimitZones.map(item => item.extra.speed_limit))], - [speedLimitZones], - ); - - // Sort in ascending order, because entries can be in random order - // This list will be displayed for users - const speedLimitListAsc = speedLimitList.sort((a, b) => a - b); - - const streetMaintenanceInfo = (colorClass, translationId) => ( -
    -
    -
    - {intl.formatMessage({ id: translationId })} -
    -
    + const renderMaintenanceSelectionList = () => ( + ); - const renderMaintenanceSelectionList = () => (openStreetMaintenanceSelectionList ? ( - <> -
    - - {intl.formatMessage({ id: 'mobilityPlatform.menu.streetMaintenance.info' })} - - {!isActiveStreetMaintenance && streetMaintenancePeriod ? ( - - ) : null} -
    - {streetMaintenanceSelections?.length > 0 - && streetMaintenanceSelections.map(item => ( -
    - item.onChangeValue(item.type)} - /> - )} - label={( - - {intl.formatMessage({ id: item.msgId })} - - )} - /> -
    - ))} -
    -
    - {streetMaintenanceInfo(classes.blue, 'mobilityPlatform.menu.streetMaintenance.info.snowplow')} - {streetMaintenanceInfo(classes.purple, 'mobilityPlatform.menu.streetMaintenance.info.deicing')} - {streetMaintenanceInfo(classes.burgundy, 'mobilityPlatform.menu.streetMaintenance.info.sandRemoval')} - {streetMaintenanceInfo(classes.green, 'mobilityPlatform.menu.streetMaintenance.info.sanitation')} -
    -
    - - ) : null); - const infoTextsWalking = [ { visible: showTrafficCounter.walking, @@ -1806,6 +1693,11 @@ const MobilitySettingsView = ({ classes, intl, navigator }) => { type: 'railwayStationsInfo', component: , }, + { + visible: showAirports, + type: 'airportInfo', + component: , + }, ]; const infoTextsRoadworks = [ @@ -1850,13 +1742,13 @@ const MobilitySettingsView = ({ classes, intl, navigator }) => { const renderWalkSettings = () => ( <> {renderSettings(openWalkSettings, walkingControlTypes)} -
    - {openCultureRouteList && !cultureRouteId ? : null} -
    + + {openCultureRouteList && !cultureRouteId ? : null} + {openCultureRouteList && (locale === 'en' || locale === 'sv') - ? renderCultureRoutes(localizedCultureRoutes) + ? renderCultureRoutes(sortedLocalizedCultureRoutes) : null} - {openCultureRouteList && locale === 'fi' ? renderCultureRoutes(cultureRouteList) : null} + {openCultureRouteList && locale === 'fi' ? renderCultureRoutes(sortedCultureRoutes) : null} {renderSelectTrailText(openMarkedTrailsList, markedTrailsObj, markedTrailsList)} { const renderBicycleSettings = () => ( <> {renderSettings(openBicycleSettings, bicycleControlTypes)} -
    - {openBicycleRouteList && !bicycleRouteName ? : null} -
    - {renderBicycleRoutes(bicycleRouteList)} + + {openBicycleRouteList && !bicycleRouteName ? : null} + + {renderBicycleRoutes(sortedBicycleRoutes)} {renderInfoTexts(infoTextsCycling)} ); @@ -1901,7 +1793,7 @@ const MobilitySettingsView = ({ classes, intl, navigator }) => { {renderSettings(openCarSettings, carControlTypes)} @@ -1957,11 +1849,7 @@ const MobilitySettingsView = ({ classes, intl, navigator }) => { * @param {*} icon * @returns JSX Element */ - const renderIcon = icon => ( -
    - {icon} -
    - ); + const renderIcon = icon => {icon}; const renderRoadworkSettings = () => ( <> @@ -1974,49 +1862,49 @@ const MobilitySettingsView = ({ classes, intl, navigator }) => { { component: renderWalkSettings(), title: intl.formatMessage({ id: 'mobilityPlatform.menu.title.walk' }), - icon: , + icon: , onClick: walkSettingsToggle, setState: openWalkSettings, }, { component: renderBicycleSettings(), title: intl.formatMessage({ id: 'mobilityPlatform.menu.title.bicycle' }), - icon: , + icon: , onClick: bicycleSettingsToggle, setState: openBicycleSettings, }, { component: renderCarSettings(), title: intl.formatMessage({ id: 'mobilityPlatform.menu.title.car' }), - icon: , + icon: , onClick: carSettingsToggle, setState: openCarSettings, }, { component: renderPublicTransportSettings(), title: intl.formatMessage({ id: 'mobilityPlatform.menu.title.public.transport' }), - icon: , + icon: , onClick: publicTransportSettingsToggle, setState: openPublicTransportSettings, }, { component: renderScooterSettings(), title: intl.formatMessage({ id: 'mobilityPlatform.menu.title.scooter' }), - icon: , + icon: , onClick: scooterSettingsToggle, setState: openScooterSettings, }, { component: renderBoatingSettings(), title: intl.formatMessage({ id: 'mobilityPlatform.menu.title.boating' }), - icon: , + icon: , onClick: boatingSettingsToggle, setState: openBoatingSettings, }, { component: renderStreetMaintenanceSettings(), title: intl.formatMessage({ id: 'mobilityPlatform.menu.title.streetMaintenance' }), - icon: , + icon: , onClick: streetMaintenanceSettingsToggle, setState: openStreetMaintenanceSettings, }, @@ -2037,24 +1925,25 @@ const MobilitySettingsView = ({ classes, intl, navigator }) => { ]; return ( -
    +
    {renderHead()} navigator.push('home')} - className={classes.topBarColor} /> - - {intl.formatMessage({ id: 'home.buttons.mobilityPlatformSettings' })} - -
    -
    -
    + + + {intl.formatMessage({ id: 'home.buttons.mobilityPlatformSettings' })} + + +
    + + {categories.map(category => ( - + { ))} -
    -
    + +
    ); }; +const StyledCheckBoxContainer = styled.div(({ theme }) => ({ + width: '100%', + backgroundColor: 'rgb(250, 250, 250)', + paddingTop: theme.spacing(1.5), + paddingBottom: theme.spacing(1.5), +})); + +const StyledIconContainer = styled.div(({ theme }) => ({ + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + width: '40px', + height: '40px', + marginRight: theme.spacing(1), +})); + +const StyledListContainer = styled.div(() => ({ + width: '100%', +})); + +const StyledListMargin = styled.div(() => ({ + marginTop: '0', +})); + +const StyledTextContainer = styled.div(({ theme }) => ({ + backgroundColor: theme.palette.primary.main, + padding: `${theme.spacing(3)} ${theme.spacing(2)}`, + paddingTop: theme.spacing(1), + color: '#fff', + textAlign: 'left', +})); + +const StyledBorderBottom = styled.div(({ isVisible }) => ({ + borderBottom: isVisible ? '1px solid rgba(193, 193, 193, 255)' : 'none', +})); + MobilitySettingsView.propTypes = { - intl: PropTypes.shape({ - formatMessage: PropTypes.func, - }).isRequired, - classes: PropTypes.objectOf(PropTypes.string).isRequired, navigator: PropTypes.objectOf(PropTypes.any), }; diff --git a/src/views/MobilitySettingsView/components/ButtonMain/ButtonMain.js b/src/views/MobilitySettingsView/components/ButtonMain/ButtonMain.js deleted file mode 100644 index 4008205b5..000000000 --- a/src/views/MobilitySettingsView/components/ButtonMain/ButtonMain.js +++ /dev/null @@ -1,57 +0,0 @@ -import { Button, Typography } from '@mui/material'; -import PropTypes from 'prop-types'; -import React from 'react'; -import { ReactSVG } from 'react-svg'; - -/** - * Render 1 or more buttons with icon and text - * @property {any} classes - * @property {any} intl - * @property {Function} onClickFunc - * @property {boolean} settingState - * @property {string} iconName - * @property {string} translationId - * @return {JSX Element} - */ - -const ButtonMain = ({ - classes, intl, onClickFunc, settingState, iconName, translationId, -}) => ( - -); - -ButtonMain.propTypes = { - classes: PropTypes.objectOf(PropTypes.any).isRequired, - intl: PropTypes.objectOf(PropTypes.any).isRequired, - onClickFunc: PropTypes.func.isRequired, - settingState: PropTypes.bool, - iconName: PropTypes.string, - translationId: PropTypes.string, -}; - -ButtonMain.defaultProps = { - settingState: false, - iconName: '', - translationId: '', -}; - -export default ButtonMain; diff --git a/src/views/MobilitySettingsView/components/ButtonMain/__tests__/ButtonMain.test.js b/src/views/MobilitySettingsView/components/ButtonMain/__tests__/ButtonMain.test.js deleted file mode 100644 index 9a8a4e67c..000000000 --- a/src/views/MobilitySettingsView/components/ButtonMain/__tests__/ButtonMain.test.js +++ /dev/null @@ -1,44 +0,0 @@ -// Link.react.test.js -import React from 'react'; -import { fireEvent } from '@testing-library/react'; -import ButtonMain from '../index'; -import { getRenderWithProviders } from '../../../../../../jestUtils'; -import finnishTranslations from '../../../../../i18n/fi'; - -const mockProps = { - settingState: false, - iconName: 'testi', - translationId: 'mobilityPlatform.menu.title.bicycle', -}; - -const renderWithProviders = getRenderWithProviders({}); - -describe('', () => { - it('should work', () => { - const { container } = renderWithProviders(); - expect(container).toMatchSnapshot(); - }); - - it('does show text correctly', () => { - const { container } = renderWithProviders(); - - const p = container.querySelectorAll('p'); - expect(p[0].textContent).toContain(finnishTranslations['mobilityPlatform.menu.title.bicycle']); - }); - - it('does contain aria-label attribute', () => { - const { container } = renderWithProviders(); - - const button = container.querySelectorAll('button'); - expect(button[0].getAttribute('aria-label')).toContain(finnishTranslations['mobilityPlatform.menu.title.bicycle']); - }); - - it('simulates click event', () => { - const mockCallBack = jest.fn(); - const { getByRole } = renderWithProviders( - , - ); - fireEvent.click(getByRole('button')); - expect(mockCallBack.mock.calls.length).toEqual(1); - }); -}); diff --git a/src/views/MobilitySettingsView/components/ButtonMain/__tests__/__snapshots__/ButtonMain.test.js.snap b/src/views/MobilitySettingsView/components/ButtonMain/__tests__/__snapshots__/ButtonMain.test.js.snap deleted file mode 100644 index b1af5526f..000000000 --- a/src/views/MobilitySettingsView/components/ButtonMain/__tests__/__snapshots__/ButtonMain.test.js.snap +++ /dev/null @@ -1,28 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[` should work 1`] = ` -
    - -
    -`; diff --git a/src/views/MobilitySettingsView/components/ButtonMain/index.js b/src/views/MobilitySettingsView/components/ButtonMain/index.js deleted file mode 100644 index 9100c328f..000000000 --- a/src/views/MobilitySettingsView/components/ButtonMain/index.js +++ /dev/null @@ -1,6 +0,0 @@ -import { withStyles } from '@mui/styles'; -import { injectIntl } from 'react-intl'; -import ButtonMain from './ButtonMain'; -import styles from './styles'; - -export default withStyles(styles)(injectIntl(ButtonMain)); diff --git a/src/views/MobilitySettingsView/components/ButtonMain/styles.js b/src/views/MobilitySettingsView/components/ButtonMain/styles.js deleted file mode 100644 index 8d9b3499c..000000000 --- a/src/views/MobilitySettingsView/components/ButtonMain/styles.js +++ /dev/null @@ -1,42 +0,0 @@ -const styles = theme => ({ - button: { - color: 'rgba(0, 0, 0, 255)', - width: '100%', - height: '3.125rem', - background: 'rgba(245, 245, 245, 255)', - textTransform: 'none', - justifyContent: 'flex-start', - borderRadius: '0', - borderTop: '1px solid rgba(0, 0, 0, 255)', - borderBottom: 'none', - '&:hover': { - background: 'rgba(230, 230, 230, 255)', - }, - }, - active: { - borderBottom: '1px solid #6f7276', - borderTop: '1px solid #6f7276', - background: 'rgba(70, 72, 75, 255)', - color: '#fff', - '&:hover': { - color: '#fff', - background: '#3e3f42', - borderBottom: '1px solid #6f7276', - borderTop: '1px solid #6f7276', - }, - }, - iconActive: { - fill: '#fff', - width: '40px', - height: '40px', - marginRight: theme.spacing(1), - }, - icon: { - fill: '#000', - width: '40px', - height: '40px', - marginRight: theme.spacing(1), - }, -}); - -export default styles; diff --git a/src/views/MobilitySettingsView/components/CityBikeInfo/CityBikeInfo.js b/src/views/MobilitySettingsView/components/CityBikeInfo/CityBikeInfo.js index d1826b024..6a6e1282d 100644 --- a/src/views/MobilitySettingsView/components/CityBikeInfo/CityBikeInfo.js +++ b/src/views/MobilitySettingsView/components/CityBikeInfo/CityBikeInfo.js @@ -1,55 +1,56 @@ import { Link, Typography } from '@mui/material'; import PropTypes from 'prop-types'; -import React, { useEffect, useState } from 'react'; -import { useSelector } from 'react-redux'; +import React from 'react'; +import styled from '@emotion/styled'; +import { useIntl } from 'react-intl'; +import useLocaleText from '../../../../utils/useLocaleText'; -const CityBikeInfo = ({ - classes, intl, bikeInfo, -}) => { - const [url, setUrl] = useState(bikeInfo.url.fi); - - const locale = useSelector(state => state.user.locale); - - useEffect(() => { - if (locale === 'en') { - setUrl(bikeInfo.url.en); - } - if (locale === 'sv') { - setUrl(bikeInfo.url.sv); - } - }, [locale]); +const CityBikeInfo = ({ bikeInfo }) => { + const intl = useIntl(); + const getLocaleText = useLocaleText(); const text = textValue => ( - - {intl.formatMessage({ - id: textValue, - })} - + + + {intl.formatMessage({ + id: textValue, + })} + + ); return ( -
    + {text(bikeInfo.paragraph1)} {text(bikeInfo.paragraph2)} {text(bikeInfo.subtitle)} - + {text(bikeInfo.link)} {text(bikeInfo.apiInfo)} -
    + ); }; +const StyledContainer = styled.div(({ theme }) => ({ + padding: `${theme.spacing(1)} ${theme.spacing(2)}`, + textAlign: 'left', +})); + +const StyledTextMargin = styled.div(({ theme }) => ({ + marginBottom: theme.spacing(1), +})); + CityBikeInfo.propTypes = { - classes: PropTypes.objectOf(PropTypes.any).isRequired, - intl: PropTypes.objectOf(PropTypes.any).isRequired, - bikeInfo: PropTypes.objectOf(PropTypes.any), + bikeInfo: PropTypes.shape({ + paragraph1: PropTypes.string, + paragraph2: PropTypes.string, + paragraph3: PropTypes.string, + subtitle: PropTypes.string, + link: PropTypes.string, + apiInfo: PropTypes.string, + url: PropTypes.objectOf(PropTypes.string), + }), }; CityBikeInfo.defaultProps = { diff --git a/src/views/MobilitySettingsView/components/CityBikeInfo/__tests__/CityBikeInfo.test.js b/src/views/MobilitySettingsView/components/CityBikeInfo/__tests__/CityBikeInfo.test.js index 2f9f653ce..bc8c72c29 100644 --- a/src/views/MobilitySettingsView/components/CityBikeInfo/__tests__/CityBikeInfo.test.js +++ b/src/views/MobilitySettingsView/components/CityBikeInfo/__tests__/CityBikeInfo.test.js @@ -42,15 +42,4 @@ describe('', () => { expect(link.textContent).toContain(finnishTranslations['mobilityPlatform.info.cityBikes.link']); expect(p[4].textContent).toContain(finnishTranslations['mobilityPlatform.info.cityBikes.apiInfo']); }); - - it('does contain aria-label attributes', () => { - const { container } = renderWithProviders(); - - const p = container.querySelectorAll('p'); - expect(p[0].getAttribute('aria-label')).toContain(finnishTranslations['mobilityPlatform.info.cityBikes.paragraph.1']); - expect(p[1].getAttribute('aria-label')).toContain(finnishTranslations['mobilityPlatform.info.cityBikes.paragraph.2']); - expect(p[2].getAttribute('aria-label')).toContain(finnishTranslations['mobilityPlatform.info.cityBikes.subtitle']); - expect(p[3].getAttribute('aria-label')).toContain(finnishTranslations['mobilityPlatform.info.cityBikes.link']); - expect(p[4].getAttribute('aria-label')).toContain(finnishTranslations['mobilityPlatform.info.cityBikes.apiInfo']); - }); }); diff --git a/src/views/MobilitySettingsView/components/CityBikeInfo/__tests__/__snapshots__/CityBikeInfo.test.js.snap b/src/views/MobilitySettingsView/components/CityBikeInfo/__tests__/__snapshots__/CityBikeInfo.test.js.snap index 63e263650..deb84fad5 100644 --- a/src/views/MobilitySettingsView/components/CityBikeInfo/__tests__/__snapshots__/CityBikeInfo.test.js.snap +++ b/src/views/MobilitySettingsView/components/CityBikeInfo/__tests__/__snapshots__/CityBikeInfo.test.js.snap @@ -3,44 +3,59 @@ exports[` should work 1`] = `
    -

    - Turun kaupunkipyörät eli tuttavallisemmin föllärit, ovat pyöriä, joita kuka vaan voi vuokrata Donkey Republicin sovelluksella. Föllärin voi vuokrata kertamaksulla, kuukausimaksulla tai koko kesän kattavalla kausimaksulla. -

    -

    + Turun kaupunkipyörät eli tuttavallisemmin föllärit, ovat pyöriä, joita kuka vaan voi vuokrata Donkey Republicin sovelluksella. Föllärin voi vuokrata kertamaksulla, kuukausimaksulla tai koko kesän kattavalla kausimaksulla. +

    +
    +
    - Jos sinulla on käytössä Fölin kausikortti, jonka kausi on vähintään 30 päivää, sisältää oikeuden käyttää fölläreitä tunnin ajan kerrallaan maksutta. Vuokrattavia pyöriä on 700 ja asemia yli 70 kappaletta. -

    -

    + Jos sinulla on käytössä Fölin kausikortti, jonka kausi on vähintään 30 päivää, sisältää oikeuden käyttää fölläreitä tunnin ajan kerrallaan maksutta. Vuokrattavia pyöriä on 700 ja asemia yli 70 kappaletta. +

    +
    +
    - Lue lisää kaupunkipyöristä: -

    +

    + Lue lisää kaupunkipyöristä: +

    +
    -

    - https://www.foli.fi/fi/aikataulut-ja-reitit/fölifillarit -

    +

    + https://www.foli.fi/fi/aikataulut-ja-reitit/fölifillarit +

    +
    -

    - Kartan tiedot tulevat Donkey Republicin rajapinnasta reaaliajassa. -

    +

    + Kartan tiedot tulevat Donkey Republicin rajapinnasta reaaliajassa. +

    +
    `; diff --git a/src/views/MobilitySettingsView/components/CityBikeInfo/index.js b/src/views/MobilitySettingsView/components/CityBikeInfo/index.js index 50cc8d81a..09ea5f16f 100644 --- a/src/views/MobilitySettingsView/components/CityBikeInfo/index.js +++ b/src/views/MobilitySettingsView/components/CityBikeInfo/index.js @@ -1,6 +1,3 @@ -import { withStyles } from '@mui/styles'; -import { injectIntl } from 'react-intl'; import CityBikeInfo from './CityBikeInfo'; -import styles from './styles'; -export default withStyles(styles)(injectIntl(CityBikeInfo)); +export default CityBikeInfo; diff --git a/src/views/MobilitySettingsView/components/CityBikeInfo/styles.js b/src/views/MobilitySettingsView/components/CityBikeInfo/styles.js deleted file mode 100644 index 440e3a413..000000000 --- a/src/views/MobilitySettingsView/components/CityBikeInfo/styles.js +++ /dev/null @@ -1,12 +0,0 @@ -const styles = theme => ({ - container: { - padding: theme.spacing(2), - textAlign: 'left', - paddingBottom: theme.spacing(1), - }, - margin: { - marginBottom: theme.spacing(1), - }, -}); - -export default styles; diff --git a/src/views/MobilitySettingsView/components/Description/Description.js b/src/views/MobilitySettingsView/components/Description/Description.js index 7d417bdb5..124ac05b8 100644 --- a/src/views/MobilitySettingsView/components/Description/Description.js +++ b/src/views/MobilitySettingsView/components/Description/Description.js @@ -1,46 +1,50 @@ import { Typography } from '@mui/material'; import PropTypes from 'prop-types'; import React from 'react'; +import styled from '@emotion/styled'; +import useLocaleText from '../../../../utils/useLocaleText'; + +const Description = ({ route }) => { + const getLocaleText = useLocaleText(); -const Description = ({ classes, route, currentLocale }) => { // Hide references to sizes of audio files. // Only finnish and english descriptions have those. - const replaceWord = inputStr => inputStr.replace(/Latauskoko ~90M|koko ~43M|Size ~6MB/gi, ''); - - const selectRouteDescription = (descriptionSv, descriptionEn, descriptionFi) => { - if (currentLocale === 'sv' && descriptionSv) { - return descriptionSv; - } - if (currentLocale === 'en' && descriptionEn) { - return replaceWord(descriptionEn); - } - return replaceWord(descriptionFi); + const replaceWord = inputStr => inputStr?.replace(/Latauskoko ~90M|koko ~43M|Size ~6MB/gi, ''); + + const descriptions = { + fi: replaceWord(route.description), + en: replaceWord(route.description_en), + sv: route.description_sv, }; return ( -
    -
    - - {selectRouteDescription(route.description_sv, route.description_en, route.description)} - -
    -
    + + + {getLocaleText(descriptions)} + + ); }; +const StyledParagraph = styled.div(({ theme }) => ({ + textAlign: 'left', + padding: theme.spacing(1.5), + width: '85%', +})); + Description.propTypes = { - classes: PropTypes.objectOf(PropTypes.any).isRequired, - route: PropTypes.objectOf(PropTypes.any), - currentLocale: PropTypes.string, + route: PropTypes.shape({ + description: PropTypes.string, + description_en: PropTypes.string, + description_sv: PropTypes.string, + }), }; Description.defaultProps = { - route: null, - currentLocale: 'fi', + route: {}, }; export default Description; diff --git a/src/views/MobilitySettingsView/components/Description/__tests__/Description.test.js b/src/views/MobilitySettingsView/components/Description/__tests__/Description.test.js index 080124905..8836f890b 100644 --- a/src/views/MobilitySettingsView/components/Description/__tests__/Description.test.js +++ b/src/views/MobilitySettingsView/components/Description/__tests__/Description.test.js @@ -2,6 +2,7 @@ import React from 'react'; import Description from '../index'; import { getRenderWithProviders } from '../../../../../../jestUtils'; +import { initialState } from '../../../../../redux/reducers/user'; const mockProps = { route: { @@ -11,7 +12,9 @@ const mockProps = { }, }; -const renderWithProviders = getRenderWithProviders({}); +const renderWithProviders = getRenderWithProviders({ + user: initialState, +}); describe('', () => { it('should work', () => { @@ -25,11 +28,4 @@ describe('', () => { const p = container.querySelectorAll('p'); expect(p[0].textContent).toEqual(mockProps.route.description); }); - - it('does contain aria-label attribute', () => { - const { container } = renderWithProviders(); - - const p = container.querySelectorAll('p'); - expect(p[0].getAttribute('aria-label')).toEqual(mockProps.route.description); - }); }); diff --git a/src/views/MobilitySettingsView/components/Description/__tests__/__snapshots__/Description.test.js.snap b/src/views/MobilitySettingsView/components/Description/__tests__/__snapshots__/Description.test.js.snap index 631650c1f..54189ad09 100644 --- a/src/views/MobilitySettingsView/components/Description/__tests__/__snapshots__/Description.test.js.snap +++ b/src/views/MobilitySettingsView/components/Description/__tests__/__snapshots__/Description.test.js.snap @@ -2,17 +2,14 @@ exports[` should work 1`] = `
    -
    -
    +

    -

    - testikuvaus -

    -
    + testikuvaus +

    `; diff --git a/src/views/MobilitySettingsView/components/Description/index.js b/src/views/MobilitySettingsView/components/Description/index.js index fb210402d..7cbdb5c55 100644 --- a/src/views/MobilitySettingsView/components/Description/index.js +++ b/src/views/MobilitySettingsView/components/Description/index.js @@ -1,6 +1,3 @@ -import { withStyles } from '@mui/styles'; -import { injectIntl } from 'react-intl'; import Description from './Description'; -import styles from './styles'; -export default withStyles(styles)(injectIntl(Description)); +export default Description; diff --git a/src/views/MobilitySettingsView/components/Description/styles.js b/src/views/MobilitySettingsView/components/Description/styles.js deleted file mode 100644 index 960cfef3e..000000000 --- a/src/views/MobilitySettingsView/components/Description/styles.js +++ /dev/null @@ -1,15 +0,0 @@ -const styles = theme => ({ - paragraph: { - textAlign: 'left', - padding: theme.spacing(1.5), - width: '85%', - }, - subtitle: { - textTransform: 'none', - }, - margin: { - marginBottom: theme.spacing(1), - }, -}); - -export default styles; diff --git a/src/views/MobilitySettingsView/components/EmptyRouteList/EmptyRouteList.js b/src/views/MobilitySettingsView/components/EmptyRouteList/EmptyRouteList.js index ef974d3df..734c1414d 100644 --- a/src/views/MobilitySettingsView/components/EmptyRouteList/EmptyRouteList.js +++ b/src/views/MobilitySettingsView/components/EmptyRouteList/EmptyRouteList.js @@ -1,42 +1,44 @@ import { Typography } from '@mui/material'; import PropTypes from 'prop-types'; import React from 'react'; +import { useIntl } from 'react-intl'; +import styled from '@emotion/styled'; /** * Check if route list is empty and render correct text - * @property {any} classes - * @property {any} intl * @property {Array} route * @return {JSX Element} */ -const EmptyRouteList = ({ classes, intl, route }) => { +const EmptyRouteList = ({ route }) => { + const intl = useIntl(); + if (route) { return ( -
    + 0 - ? intl.formatMessage({ id: 'mobilityPlatform.menu.routes.info' }) - : intl.formatMessage({ id: 'mobilityPlatform.menu.routes.emptyList' }) - } > {route.length > 0 ? intl.formatMessage({ id: 'mobilityPlatform.menu.routes.info' }) : intl.formatMessage({ id: 'mobilityPlatform.menu.routes.emptyList' })} -
    + ); } return null; }; +const StyledParagraph = styled.div(({ theme }) => ({ + textAlign: 'left', + padding: theme.spacing(1.5), +})); + EmptyRouteList.propTypes = { - classes: PropTypes.objectOf(PropTypes.any).isRequired, - intl: PropTypes.objectOf(PropTypes.any).isRequired, - route: PropTypes.arrayOf(PropTypes.any), + route: PropTypes.arrayOf(PropTypes.shape({ + name: PropTypes.string, + })), }; EmptyRouteList.defaultProps = { diff --git a/src/views/MobilitySettingsView/components/EmptyRouteList/__tests__/EmptyRouteList.test.js b/src/views/MobilitySettingsView/components/EmptyRouteList/__tests__/EmptyRouteList.test.js index 22e41630e..077d57df5 100644 --- a/src/views/MobilitySettingsView/components/EmptyRouteList/__tests__/EmptyRouteList.test.js +++ b/src/views/MobilitySettingsView/components/EmptyRouteList/__tests__/EmptyRouteList.test.js @@ -23,11 +23,4 @@ describe('', () => { const p = container.querySelectorAll('p'); expect(p[0].textContent).toContain(finnishTranslations['mobilityPlatform.menu.routes.emptyList']); }); - - it('does contain aria-label attributes', () => { - const { container } = renderWithProviders(); - - const p = container.querySelectorAll('p'); - expect(p[0].getAttribute('aria-label')).toContain(finnishTranslations['mobilityPlatform.menu.routes.emptyList']); - }); }); diff --git a/src/views/MobilitySettingsView/components/EmptyRouteList/__tests__/__snapshots__/EmptyRouteList.test.js.snap b/src/views/MobilitySettingsView/components/EmptyRouteList/__tests__/__snapshots__/EmptyRouteList.test.js.snap index a178c4e08..db2d02ed4 100644 --- a/src/views/MobilitySettingsView/components/EmptyRouteList/__tests__/__snapshots__/EmptyRouteList.test.js.snap +++ b/src/views/MobilitySettingsView/components/EmptyRouteList/__tests__/__snapshots__/EmptyRouteList.test.js.snap @@ -3,10 +3,9 @@ exports[` should work 1`] = `

    Reittejä ladataan. diff --git a/src/views/MobilitySettingsView/components/EmptyRouteList/index.js b/src/views/MobilitySettingsView/components/EmptyRouteList/index.js index b4e3b57c7..6ec1e263d 100644 --- a/src/views/MobilitySettingsView/components/EmptyRouteList/index.js +++ b/src/views/MobilitySettingsView/components/EmptyRouteList/index.js @@ -1,6 +1,3 @@ -import { withStyles } from '@mui/styles'; -import { injectIntl } from 'react-intl'; import EmptyRouteList from './EmptyRouteList'; -import styles from './styles'; -export default withStyles(styles)(injectIntl(EmptyRouteList)); +export default EmptyRouteList; diff --git a/src/views/MobilitySettingsView/components/EmptyRouteList/styles.js b/src/views/MobilitySettingsView/components/EmptyRouteList/styles.js deleted file mode 100644 index ced94006d..000000000 --- a/src/views/MobilitySettingsView/components/EmptyRouteList/styles.js +++ /dev/null @@ -1,8 +0,0 @@ -const styles = theme => ({ - paragraph: { - textAlign: 'left', - padding: theme.spacing(1.5), - }, -}); - -export default styles; diff --git a/src/views/MobilitySettingsView/components/ExtendedInfo/ExtendedInfo.js b/src/views/MobilitySettingsView/components/ExtendedInfo/ExtendedInfo.js index 8f33f764b..ddc6cdd10 100644 --- a/src/views/MobilitySettingsView/components/ExtendedInfo/ExtendedInfo.js +++ b/src/views/MobilitySettingsView/components/ExtendedInfo/ExtendedInfo.js @@ -1,42 +1,62 @@ import { Typography } from '@mui/material'; import PropTypes from 'prop-types'; import React from 'react'; +import { useIntl } from 'react-intl'; +import styled from '@emotion/styled'; -const ExtendedInfo = ({ classes, intl, translations }) => { - const text = (message, props = {}) => ( - - {intl.formatMessage({ - id: message, - })} - +const ExtendedInfo = ({ translations }) => { + const intl = useIntl(); + + const text = (message, isMargin) => ( + + + {intl.formatMessage({ + id: message, + })} + + ); return ( -

    + {text(translations.message1)} -
      + {translations.zones.map(item => (
    • {text(item)}
    • ))} -
    - {text(translations.message2, { className: classes.margin })} + + {text(translations.message2, true)} {text(translations.message3)} -
    + ); }; +const StyledContainer = styled.div(({ theme }) => ({ + margin: theme.spacing(2), + textAlign: 'left', + paddingBottom: theme.spacing(2), +})); + +const StyledList = styled.ul(({ theme }) => ({ + marginTop: theme.spacing(1), + marginBottom: theme.spacing(1), +})); + +const StyledTextContainer = styled.div(({ theme, isMargin }) => ({ + marginBottom: isMargin ? theme.spacing(1) : 0, +})); + ExtendedInfo.propTypes = { - classes: PropTypes.objectOf(PropTypes.any).isRequired, - intl: PropTypes.objectOf(PropTypes.any).isRequired, - translations: PropTypes.objectOf(PropTypes.any), + translations: PropTypes.shape({ + message1: PropTypes.string, + message2: PropTypes.string, + message3: PropTypes.string, + zones: PropTypes.arrayOf(PropTypes.string), + }), }; ExtendedInfo.defaultProps = { diff --git a/src/views/MobilitySettingsView/components/ExtendedInfo/__tests__/ExtendedInfo.test.js b/src/views/MobilitySettingsView/components/ExtendedInfo/__tests__/ExtendedInfo.test.js index c13d8c08f..c4bdee8cf 100644 --- a/src/views/MobilitySettingsView/components/ExtendedInfo/__tests__/ExtendedInfo.test.js +++ b/src/views/MobilitySettingsView/components/ExtendedInfo/__tests__/ExtendedInfo.test.js @@ -38,16 +38,4 @@ describe('', () => { expect(list[1].textContent).toContain(finnishTranslations['mobilityPlatform.info.parkingChargeZones.zone.2']); expect(list[2].textContent).toContain(finnishTranslations['mobilityPlatform.info.parkingChargeZones.zone.3']); }); - - it('does contain aria-label attributes', () => { - const { container } = renderWithProviders(); - - const p = container.querySelectorAll('p'); - expect(p[0].getAttribute('aria-label')).toContain(finnishTranslations['mobilityPlatform.info.parkingChargeZones.paragraph.1']); - expect(p[1].getAttribute('aria-label')).toContain(finnishTranslations['mobilityPlatform.info.parkingChargeZones.zone.1']); - expect(p[2].getAttribute('aria-label')).toContain(finnishTranslations['mobilityPlatform.info.parkingChargeZones.zone.2']); - expect(p[3].getAttribute('aria-label')).toContain(finnishTranslations['mobilityPlatform.info.parkingChargeZones.zone.3']); - expect(p[4].getAttribute('aria-label')).toContain(finnishTranslations['mobilityPlatform.info.parkingChargeZones.paragraph.2']); - expect(p[5].getAttribute('aria-label')).toContain(finnishTranslations['mobilityPlatform.info.parkingChargeZones.paragraph.3']); - }); }); diff --git a/src/views/MobilitySettingsView/components/ExtendedInfo/__tests__/__snapshots__/ExtendedInfo.test.js.snap b/src/views/MobilitySettingsView/components/ExtendedInfo/__tests__/__snapshots__/ExtendedInfo.test.js.snap index 902224317..c90b61cec 100644 --- a/src/views/MobilitySettingsView/components/ExtendedInfo/__tests__/__snapshots__/ExtendedInfo.test.js.snap +++ b/src/views/MobilitySettingsView/components/ExtendedInfo/__tests__/__snapshots__/ExtendedInfo.test.js.snap @@ -3,54 +3,72 @@ exports[` should work 1`] = `
    -

    - Turussa on käytössä kolme eri vyöhykettä, joilla on eri tuntimaksut. -

    +

    + Turussa on käytössä kolme eri vyöhykettä, joilla on eri tuntimaksut. +

    +
    • -

      - Ensimmäinen vyöhyke (ydinkeskusta-alue): 3,60 €/tunti (1.1.2023 alkaen) -

      +

      + Ensimmäinen vyöhyke (ydinkeskusta-alue): 3,60 €/tunti (1.1.2023 alkaen) +

      +
  • -

    - Toinen vyöhyke : 1,80 €/tunti (1.1.2023 alkaen) -

    +

    + Toinen vyöhyke : 1,80 €/tunti (1.1.2023 alkaen) +

    +
  • -

    - Kolmas vyöhyke: 0,60 €/tunti -

    +

    + Kolmas vyöhyke: 0,60 €/tunti +

    +
  • -

    - 3. vyöhyke on voimassa 2. vyöhykkeen rajojen, sekä kaupungin rajojen välisellä alueella. -

    -

    + 3. vyöhyke on voimassa 2. vyöhykkeen rajojen, sekä kaupungin rajojen välisellä alueella. +

    +
    +
    - Maksullisuus määräytyy kuitenkin aina voimassa olevien liikennemerkkien mukaisesti ja koskee Turun kaupungin katutilaa ja kaupungin omia alueita, kuten kauppahallia ja kaupungintaloa. -

    +

    + Maksullisuus määräytyy kuitenkin aina voimassa olevien liikennemerkkien mukaisesti ja koskee Turun kaupungin katutilaa ja kaupungin omia alueita, kuten kauppahallia ja kaupungintaloa. +

    +
    `; diff --git a/src/views/MobilitySettingsView/components/ExtendedInfo/index.js b/src/views/MobilitySettingsView/components/ExtendedInfo/index.js index 197088f25..123f2a6ba 100644 --- a/src/views/MobilitySettingsView/components/ExtendedInfo/index.js +++ b/src/views/MobilitySettingsView/components/ExtendedInfo/index.js @@ -1,6 +1,3 @@ -import { withStyles } from '@mui/styles'; -import { injectIntl } from 'react-intl'; import ExtendedInfo from './ExtendedInfo'; -import styles from './styles'; -export default withStyles(styles)(injectIntl(ExtendedInfo)); +export default ExtendedInfo; diff --git a/src/views/MobilitySettingsView/components/ExtendedInfo/styles.js b/src/views/MobilitySettingsView/components/ExtendedInfo/styles.js deleted file mode 100644 index 1717c2df1..000000000 --- a/src/views/MobilitySettingsView/components/ExtendedInfo/styles.js +++ /dev/null @@ -1,14 +0,0 @@ -export default theme => ({ - container: { - margin: theme.spacing(2), - textAlign: 'left', - paddingBottom: theme.spacing(2), - }, - margin: { - marginBottom: theme.spacing(1), - }, - list: { - marginTop: theme.spacing(1), - marginBottom: theme.spacing(1), - }, -}); diff --git a/src/views/MobilitySettingsView/components/FormLabel/FormLabel.js b/src/views/MobilitySettingsView/components/FormLabel/FormLabel.js deleted file mode 100644 index aaa3fa523..000000000 --- a/src/views/MobilitySettingsView/components/FormLabel/FormLabel.js +++ /dev/null @@ -1,73 +0,0 @@ -import { FormControlLabel, Switch, Typography } from '@mui/material'; -import useMediaQuery from '@mui/material/useMediaQuery'; -import PropTypes from 'prop-types'; -import React from 'react'; - -/** - * Render 1 or more switches inside form. - * @property {any} classes - * @property {any} intl - * @property {string} keyVal - * @property {string} msgId - * @property {boolean} checkedValue - * @property {Function} onChangeValue - * @return {JSX Element} - */ - -const FormLabel = ({ - classes, intl, msgId, checkedValue, onChangeValue, -}) => { - const useMobileStatus = () => useMediaQuery('(max-width:360px)'); - - const isNarrow = useMobileStatus(); - - return ( - - {intl.formatMessage({ - id: msgId, - })} - - )} - control={( - onChangeValue()} - onKeyPress={(event) => { - if (event.key === 'Enter') { - onChangeValue(); - } - }} - /> - )} - className={`${classes.formLabel} ${isNarrow ? classes.paddingSm : classes.paddingMd}`} - /> - ); -}; - -FormLabel.propTypes = { - classes: PropTypes.objectOf(PropTypes.any).isRequired, - intl: PropTypes.objectOf(PropTypes.any).isRequired, - msgId: PropTypes.string, - checkedValue: PropTypes.bool, - onChangeValue: PropTypes.func.isRequired, -}; - -FormLabel.defaultProps = { - msgId: '', - checkedValue: false, -}; - -export default FormLabel; diff --git a/src/views/MobilitySettingsView/components/FormLabel/__tests__/FormLabel.test.js b/src/views/MobilitySettingsView/components/FormLabel/__tests__/FormLabel.test.js deleted file mode 100644 index 7e9589bfb..000000000 --- a/src/views/MobilitySettingsView/components/FormLabel/__tests__/FormLabel.test.js +++ /dev/null @@ -1,33 +0,0 @@ -// Link.react.test.js -import React from 'react'; -import FormLabel from '../index'; -import { getRenderWithProviders } from '../../../../../../jestUtils'; -import finnishTranslations from '../../../../../i18n/fi'; - -const mockProps = { - msgId: 'mobilityPlatform.menu.showBicycleStands', - checkedValue: false, -}; - -const renderWithProviders = getRenderWithProviders({}); - -describe('', () => { - it('should work', () => { - const { container } = renderWithProviders(); - expect(container).toMatchSnapshot(); - }); - - it('does show text correctly', () => { - const { container } = renderWithProviders(); - - const p = container.querySelectorAll('p'); - expect(p[0].textContent).toContain(finnishTranslations['mobilityPlatform.menu.showBicycleStands']); - }); - - it('does contain aria-label attribute', () => { - const { container } = renderWithProviders(); - - const p = container.querySelectorAll('p'); - expect(p[0].getAttribute('aria-label')).toContain(finnishTranslations['mobilityPlatform.menu.showBicycleStands']); - }); -}); diff --git a/src/views/MobilitySettingsView/components/FormLabel/__tests__/__snapshots__/FormLabel.test.js.snap b/src/views/MobilitySettingsView/components/FormLabel/__tests__/__snapshots__/FormLabel.test.js.snap deleted file mode 100644 index 172c1b129..000000000 --- a/src/views/MobilitySettingsView/components/FormLabel/__tests__/__snapshots__/FormLabel.test.js.snap +++ /dev/null @@ -1,36 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[` should work 1`] = ` -
    - -
    -`; diff --git a/src/views/MobilitySettingsView/components/FormLabel/index.js b/src/views/MobilitySettingsView/components/FormLabel/index.js deleted file mode 100644 index 9da6695f0..000000000 --- a/src/views/MobilitySettingsView/components/FormLabel/index.js +++ /dev/null @@ -1,6 +0,0 @@ -import { withStyles } from '@mui/styles'; -import { injectIntl } from 'react-intl'; -import FormLabel from './FormLabel'; -import styles from './styles'; - -export default withStyles(styles)(injectIntl(FormLabel)); diff --git a/src/views/MobilitySettingsView/components/FormLabel/styles.js b/src/views/MobilitySettingsView/components/FormLabel/styles.js deleted file mode 100644 index 4cd004043..000000000 --- a/src/views/MobilitySettingsView/components/FormLabel/styles.js +++ /dev/null @@ -1,15 +0,0 @@ -const styles = { - formLabel: { - padding: '0.4rem 3.5rem', - margin: '0', - color: 'rgba(0, 0, 0, 255)', - }, - paddingMd: { - padding: '0.4rem 3rem', - }, - paddingSm: { - padding: '0.4rem 1rem', - }, -}; - -export default styles; diff --git a/src/views/MobilitySettingsView/components/MobilityToggleButton/MobilityToggleButton.js b/src/views/MobilitySettingsView/components/MobilityToggleButton/MobilityToggleButton.js index 42c651943..86053b8b2 100644 --- a/src/views/MobilitySettingsView/components/MobilityToggleButton/MobilityToggleButton.js +++ b/src/views/MobilitySettingsView/components/MobilityToggleButton/MobilityToggleButton.js @@ -1,58 +1,80 @@ import { Typography } from '@mui/material'; import PropTypes from 'prop-types'; import React from 'react'; +import styled from '@emotion/styled'; +import { useIntl } from 'react-intl'; +import { css } from '@emotion/css'; import { SMSwitch } from '../../../../components'; /** * Render 1 or more switches inside form. - * @property {any} classes - * @property {any} intl - * @property {string} keyVal * @property {string} msgId * @property {boolean} checkedValue * @property {Function} onChangeValue + * @property {number} selectionSize * @return {JSX Element} */ const MobilityToggleButton = ({ - classes, intl, msgId, checkedValue, onChangeValue, selectionSize, inputProps, ...rest -}) => ( -
    - onChangeValue(e)} - checked={checkedValue} - {...rest} - /> -
    - - {intl.formatMessage({ - id: msgId, - })} - -
    -
    -); + msgId, checkedValue, onChangeValue, selectionSize, inputProps, ...rest +}) => { + const intl = useIntl(); + + const switchBorderClass = css({ + border: '1px solid #949494', + }); + + return ( + + onChangeValue(e)} + checked={checkedValue} + {...rest} + /> + + + {intl.formatMessage({ + id: msgId, + })} + + + + ); +}; + +const StyledSwitchContainer = styled.div(({ theme }) => ({ + paddingLeft: theme.spacing(2), + display: 'inline-flex', + alignItems: 'center', + marginLeft: theme.spacing(2), + verticalAlign: 'middle', +})); + +const StyledSMSwitch = styled(SMSwitch)(() => ({ + overflow: 'visible', +})); + +const StyledLabelContainer = styled.div(({ theme }) => ({ + marginLeft: theme.spacing(2), +})); MobilityToggleButton.propTypes = { - classes: PropTypes.objectOf(PropTypes.any).isRequired, - intl: PropTypes.objectOf(PropTypes.any).isRequired, msgId: PropTypes.string, checkedValue: PropTypes.bool, onChangeValue: PropTypes.func.isRequired, diff --git a/src/views/MobilitySettingsView/components/MobilityToggleButton/index.js b/src/views/MobilitySettingsView/components/MobilityToggleButton/index.js index 8a3d2b62e..d78df1284 100644 --- a/src/views/MobilitySettingsView/components/MobilityToggleButton/index.js +++ b/src/views/MobilitySettingsView/components/MobilityToggleButton/index.js @@ -1,6 +1,3 @@ -import { withStyles } from '@mui/styles'; -import { injectIntl } from 'react-intl'; import MobilityToggleButton from './MobilityToggleButton'; -import styles from './styles'; -export default withStyles(styles)(injectIntl(MobilityToggleButton)); +export default MobilityToggleButton; diff --git a/src/views/MobilitySettingsView/components/MobilityToggleButton/styles.js b/src/views/MobilitySettingsView/components/MobilityToggleButton/styles.js deleted file mode 100644 index 83f577cc9..000000000 --- a/src/views/MobilitySettingsView/components/MobilityToggleButton/styles.js +++ /dev/null @@ -1,20 +0,0 @@ -const styles = (theme) => ({ - mobilityMapSwitch: { - paddingLeft: theme.spacing(2), - display: 'inline-flex', - alignItems: 'center', - marginLeft: theme.spacing(2), - verticalAlign: 'middle', - }, - labelContainer: { - marginLeft: theme.spacing(2), - }, - customSwitch: { - overflow: 'visible', - }, - switchBorder: { - border: '1px solid #949494', - }, -}); - -export default styles; diff --git a/src/views/MobilitySettingsView/components/Pagination/Pagination.js b/src/views/MobilitySettingsView/components/Pagination/Pagination.js index d1b0beb24..31d77ca2b 100644 --- a/src/views/MobilitySettingsView/components/Pagination/Pagination.js +++ b/src/views/MobilitySettingsView/components/Pagination/Pagination.js @@ -1,42 +1,65 @@ import React from 'react'; import PropTypes from 'prop-types'; -import SMButton from '../../../../components/ServiceMapButton'; -import Container from '../../../../components/Container'; +import { useIntl } from 'react-intl'; +import styled from '@emotion/styled'; +import { ButtonBase } from '@mui/material'; /** Basic pagination functional component. * Renders number on buttons based on the length of items array and items per page value. * */ - const Pagination = ({ - classes, intl, items, itemsPerPage, currentPage, setCurrentPage, + items, itemsPerPage, currentPage, setCurrentPage, }) => { + const intl = useIntl(); const totalPages = Math.ceil(items.length / itemsPerPage); const paginationLinks = []; for (let i = 1; i <= totalPages; i += 1) { paginationLinks.push( - setCurrentPage(i)} - className={`${classes.button} ${currentPage === i ? classes.active : ''}`} variant="contained" role="link" > {i} - , + , ); } - return {paginationLinks}; + return {paginationLinks}; }; +const StyledContainer = styled.div(({ theme }) => ({ + flexDirection: 'row', + margin: theme.spacing(1, 2), + padding: theme.spacing(0.5), +})); + +const StyledButton = styled(({ isActive, ...props }) => )(({ theme, isActive }) => ({ + margin: theme.spacing(0.5), + height: 32, + width: 32, + minHeight: 32, + minWidth: 32, + border: `1px solid ${theme.palette.white.contrastText}`, + borderRadius: 4, + backgroundColor: isActive ? theme.palette.primary.main : theme.palette.white.main, + color: isActive ? theme.palette.white.main : 'rgb(0, 0, 0)', + '&:hover': { + backgroundColor: isActive ? theme.palette.primary.main : '', + color: isActive ? theme.palette.white.main : '', + }, +})); + Pagination.propTypes = { - classes: PropTypes.objectOf(PropTypes.any).isRequired, - intl: PropTypes.shape({ - formatMessage: PropTypes.func, - }).isRequired, - items: PropTypes.arrayOf(PropTypes.any), + items: PropTypes.arrayOf( + PropTypes.shape({ + name: PropTypes.string, + }), + ), itemsPerPage: PropTypes.number, currentPage: PropTypes.number, setCurrentPage: PropTypes.func.isRequired, diff --git a/src/views/MobilitySettingsView/components/Pagination/index.js b/src/views/MobilitySettingsView/components/Pagination/index.js index acc70f224..9ed530b1f 100644 --- a/src/views/MobilitySettingsView/components/Pagination/index.js +++ b/src/views/MobilitySettingsView/components/Pagination/index.js @@ -1,6 +1,3 @@ -import { withStyles } from '@mui/styles'; -import { injectIntl } from 'react-intl'; import Pagination from './Pagination'; -import styles from './styles'; -export default withStyles(styles)(injectIntl(Pagination)); +export default Pagination; diff --git a/src/views/MobilitySettingsView/components/Pagination/styles.js b/src/views/MobilitySettingsView/components/Pagination/styles.js deleted file mode 100644 index 5d032d5d9..000000000 --- a/src/views/MobilitySettingsView/components/Pagination/styles.js +++ /dev/null @@ -1,26 +0,0 @@ -const styles = theme => ({ - pagination: { - flexDirection: 'row', - margin: theme.spacing(1, 2), - padding: theme.spacing(0.5), - }, - button: { - margin: theme.spacing(0.5), - height: 32, - width: 32, - minHeight: 32, - minWidth: 32, - backgroundColor: theme.palette.white.main, - color: 'rgb(0, 0, 0)', - }, - active: { - backgroundColor: theme.palette.primary.main, - color: theme.palette.white.main, - '&:hover': { - backgroundColor: theme.palette.primary.main, - color: theme.palette.white.main, - }, - }, -}); - -export default styles; diff --git a/src/views/MobilitySettingsView/components/ParkingChargeZoneList/ParkingChargeZoneList.js b/src/views/MobilitySettingsView/components/ParkingChargeZoneList/ParkingChargeZoneList.js index bc7facc34..5f5535a41 100644 --- a/src/views/MobilitySettingsView/components/ParkingChargeZoneList/ParkingChargeZoneList.js +++ b/src/views/MobilitySettingsView/components/ParkingChargeZoneList/ParkingChargeZoneList.js @@ -1,33 +1,31 @@ import React from 'react'; import PropTypes from 'prop-types'; import { Checkbox, FormControlLabel, Typography } from '@mui/material'; +import { useIntl } from 'react-intl'; import { isDataValid } from '../../../../components/MobilityPlatform/utils/utils'; +import { StyledCheckboxItem } from '../styled/styled'; const ParkingChargeZoneList = ({ - intl, classes, openZoneList, parkingChargeZones, zoneId, selectZone, + openZoneList, parkingChargeZones, zoneId, selectZone, }) => { + const intl = useIntl(); const renderData = isDataValid(openZoneList, parkingChargeZones); return ( renderData ? parkingChargeZones.map(item => ( -
    + selectZone(item.id)} /> )} label={( {intl.formatMessage( { id: 'mobilityPlatform.menu.parkingChargeZones.subtitle' }, @@ -36,17 +34,20 @@ const ParkingChargeZoneList = ({ )} /> -
    + )) : null ); }; ParkingChargeZoneList.propTypes = { - intl: PropTypes.objectOf(PropTypes.any).isRequired, - classes: PropTypes.objectOf(PropTypes.any).isRequired, openZoneList: PropTypes.bool, - parkingChargeZones: PropTypes.arrayOf(PropTypes.object), + parkingChargeZones: PropTypes.arrayOf(PropTypes.shape({ + id: PropTypes.string, + extra: PropTypes.shape({ + maksuvyohyke: PropTypes.string, + }), + })), zoneId: PropTypes.string, selectZone: PropTypes.func.isRequired, }; diff --git a/src/views/MobilitySettingsView/components/ParkingChargeZoneList/index.js b/src/views/MobilitySettingsView/components/ParkingChargeZoneList/index.js index 896a25934..009f3bf13 100644 --- a/src/views/MobilitySettingsView/components/ParkingChargeZoneList/index.js +++ b/src/views/MobilitySettingsView/components/ParkingChargeZoneList/index.js @@ -1,6 +1,3 @@ -import { withStyles } from '@mui/styles'; -import { injectIntl } from 'react-intl'; import ParkingChargeZoneList from './ParkingChargeZoneList'; -import styles from './styles'; -export default withStyles(styles)(injectIntl(ParkingChargeZoneList)); +export default ParkingChargeZoneList; diff --git a/src/views/MobilitySettingsView/components/ParkingChargeZoneList/styles.js b/src/views/MobilitySettingsView/components/ParkingChargeZoneList/styles.js deleted file mode 100644 index de6e48c4e..000000000 --- a/src/views/MobilitySettingsView/components/ParkingChargeZoneList/styles.js +++ /dev/null @@ -1,11 +0,0 @@ -const styles = theme => ({ - checkBoxContainer: { - borderBottom: '1px solid #6f7276', - display: 'flex', - flexDirection: 'column', - justifyContent: 'start', - paddingLeft: theme.spacing(3.5), - }, -}); - -export default styles; diff --git a/src/views/MobilitySettingsView/components/RouteLength/RouteLength.js b/src/views/MobilitySettingsView/components/RouteLength/RouteLength.js index 717b46e45..88c87c07a 100644 --- a/src/views/MobilitySettingsView/components/RouteLength/RouteLength.js +++ b/src/views/MobilitySettingsView/components/RouteLength/RouteLength.js @@ -1,98 +1,85 @@ import { Typography } from '@mui/material'; import PropTypes from 'prop-types'; import React from 'react'; +import { useIntl } from 'react-intl'; +import styled from '@emotion/styled'; + +const RouteLength = ({ route }) => { + const intl = useIntl(); -const RouteLength = ({ classes, intl, route }) => { const formatRoutelength = inputLength => Math.round(inputLength / 1000); - const renderRouteText = (routeName) => { + const renderRouteText = routeName => { switch (routeName) { case 'EuroVelo': return ( - + {intl.formatMessage({ id: 'mobilityPlatform.menu.bicycleRoutes.euroVelo' })} - + ); case 'Saariston rengastie': return ( - + {intl.formatMessage({ id: 'mobilityPlatform.menu.bicycleRoutes.archipelagoTrail' })} - + ); case 'Aurajoentie': return ( - + {intl.formatMessage({ id: 'mobilityPlatform.menu.bicycleRoutes.auraRiverTrail' })} - + ); default: return null; } }; - const generateTranslations = (routeName) => { + const generateTranslations = routeName => { const split = routeName.split(' '); const [a, b] = split; if (a === 'Seutureitti') { return ( - + {intl.formatMessage({ id: `mobilityPlatform.menu.bicycleRoutes.regionalTrail${b}` })} - + ); } return renderRouteText(routeName); }; return ( -
    -
    - - {intl.formatMessage({ id: 'mobilityPlatform.menu.bicycleRoutes.length' })} - {' '} - {formatRoutelength(route.length)} - {' '} - km. - - {generateTranslations(route.name_fi)} -
    -
    + + + {intl.formatMessage( + { id: 'mobilityPlatform.menu.bicycleRoutes.length' }, + { value: formatRoutelength(route.length) }, + )} + + {generateTranslations(route.name_fi)} + ); }; +const StyledTypography = styled(Typography)(({ theme }) => ({ + marginTop: theme.spacing(1), +})); + +const StyledContainer = styled.div(({ theme }) => ({ + textAlign: 'left', + padding: theme.spacing(1.5), + width: '85%', + marginLeft: theme.spacing(3), +})); + RouteLength.propTypes = { - classes: PropTypes.objectOf(PropTypes.any).isRequired, - intl: PropTypes.objectOf(PropTypes.any).isRequired, - route: PropTypes.objectOf(PropTypes.any), + route: PropTypes.shape({ + name_fi: PropTypes.string, + length: PropTypes.number, + }), }; RouteLength.defaultProps = { - route: null, + route: {}, }; export default RouteLength; diff --git a/src/views/MobilitySettingsView/components/RouteLength/__tests__/RouteLength.test.js b/src/views/MobilitySettingsView/components/RouteLength/__tests__/RouteLength.test.js index 58122a9f6..beda6dad6 100644 --- a/src/views/MobilitySettingsView/components/RouteLength/__tests__/RouteLength.test.js +++ b/src/views/MobilitySettingsView/components/RouteLength/__tests__/RouteLength.test.js @@ -23,15 +23,7 @@ describe('', () => { const { container } = renderWithProviders(); const p = container.querySelectorAll('p'); - expect(p[0].textContent).toContain(`${finnishTranslations['mobilityPlatform.menu.bicycleRoutes.length']} 100 km.`); + expect(p[0].textContent).toContain(`${finnishTranslations['mobilityPlatform.menu.bicycleRoutes.length'].replace('{value}', '100')}`); expect(p[1]).toBeInTheDocument(); }); - - it('does contain aria-label attribute', () => { - const { container } = renderWithProviders(); - - const p = container.querySelectorAll('p'); - expect(p[0].getAttribute('aria-label')).toContain(`${finnishTranslations['mobilityPlatform.menu.bicycleRoutes.length']} 100 km.`); - expect(p[1].getAttribute('aria-label')).toBeTruthy(); - }); }); diff --git a/src/views/MobilitySettingsView/components/RouteLength/__tests__/__snapshots__/RouteLength.test.js.snap b/src/views/MobilitySettingsView/components/RouteLength/__tests__/__snapshots__/RouteLength.test.js.snap index 2f582ce1e..192a421b5 100644 --- a/src/views/MobilitySettingsView/components/RouteLength/__tests__/__snapshots__/RouteLength.test.js.snap +++ b/src/views/MobilitySettingsView/components/RouteLength/__tests__/__snapshots__/RouteLength.test.js.snap @@ -2,27 +2,19 @@ exports[` should work 1`] = `
    -
    -
    +

    -

    - Reitin pituus: - - 100 - - km. -

    -

    - EuroVelo 10 on eurooppalainen Suomen rannikkoa seuraava polkupyöräreitti. Helsingin ja Turun välisellä matkalla reitti on merkitty opastein. -

    -
    + Reitin pituus: 100 km. +

    +

    + EuroVelo 10 on eurooppalainen Suomen rannikkoa seuraava polkupyöräreitti. Helsingin ja Turun välisellä matkalla reitti on merkitty opastein. +

    `; diff --git a/src/views/MobilitySettingsView/components/RouteLength/index.js b/src/views/MobilitySettingsView/components/RouteLength/index.js index 70bc22995..614d1cc3b 100644 --- a/src/views/MobilitySettingsView/components/RouteLength/index.js +++ b/src/views/MobilitySettingsView/components/RouteLength/index.js @@ -1,6 +1,3 @@ -import { withStyles } from '@mui/styles'; -import { injectIntl } from 'react-intl'; import RouteLength from './RouteLength'; -import styles from './styles'; -export default withStyles(styles)(injectIntl(RouteLength)); +export default RouteLength; diff --git a/src/views/MobilitySettingsView/components/RouteLength/styles.js b/src/views/MobilitySettingsView/components/RouteLength/styles.js deleted file mode 100644 index 6d019b6c7..000000000 --- a/src/views/MobilitySettingsView/components/RouteLength/styles.js +++ /dev/null @@ -1,13 +0,0 @@ -const styles = theme => ({ - paragraph: { - textAlign: 'left', - padding: theme.spacing(1.5), - width: '85%', - marginLeft: theme.spacing(3), - }, - margin: { - marginTop: theme.spacing(1), - }, -}); - -export default styles; diff --git a/src/views/MobilitySettingsView/components/RouteList/RouteList.js b/src/views/MobilitySettingsView/components/RouteList/RouteList.js index 31029c228..b21e6a87c 100644 --- a/src/views/MobilitySettingsView/components/RouteList/RouteList.js +++ b/src/views/MobilitySettingsView/components/RouteList/RouteList.js @@ -3,19 +3,18 @@ import { Checkbox, FormControlLabel, Typography } from '@mui/material'; import PropTypes from 'prop-types'; import useLocaleText from '../../../../utils/useLocaleText'; import { isDataValid } from '../../../../components/MobilityPlatform/utils/utils'; +import { StyledCheckboxItem } from '../styled/styled'; import RouteLength from '../RouteLength'; import Description from '../Description'; import Pagination from '../Pagination'; const RouteList = ({ - classes, openList, items, itemsPerPage, routeAttr, type, setRouteState, - locale, }) => { const [currentPage, setCurrentPage] = useState(1); const getLocaleText = useLocaleText(); @@ -24,7 +23,7 @@ const RouteList = ({ const isListValid = isDataValid(openList, items); - const renderContent = (item) => { + const renderContent = item => { if (type === 'BicycleRoute') { return ( item.name_fi === routeAttr ? : null @@ -32,7 +31,7 @@ const RouteList = ({ } if (type === 'CultureRoute') { return ( - item.id === routeAttr ? : null + item.id === routeAttr ? : null ); } return null; @@ -45,13 +44,12 @@ const RouteList = ({ return isListValid ? paginatedItems.map(item => ( -
    + setRouteState(type === 'BicycleRoute' ? item.name_fi : item.id)} /> )} @@ -62,14 +60,14 @@ const RouteList = ({ )} /> {renderContent(item)} -
    + )) : null; }; return (
    -
    {renderList()}
    +
    {renderList()}
    {openList ? ( ({ - checkBoxItem: { - borderBottom: '1px solid rgb(193, 193, 193)', - display: 'flex', - flexDirection: 'column', - justifyContent: 'start', - paddingLeft: theme.spacing(3.5), - }, -}); - -export default styles; diff --git a/src/views/MobilitySettingsView/components/ScooterProviderList/ScooterProviderList.js b/src/views/MobilitySettingsView/components/ScooterProviderList/ScooterProviderList.js index 46199c282..76a32f510 100644 --- a/src/views/MobilitySettingsView/components/ScooterProviderList/ScooterProviderList.js +++ b/src/views/MobilitySettingsView/components/ScooterProviderList/ScooterProviderList.js @@ -1,32 +1,32 @@ import React from 'react'; import { Checkbox, FormControlLabel, Typography } from '@mui/material'; import PropTypes from 'prop-types'; +import { useIntl } from 'react-intl'; +import styled from '@emotion/styled'; -const ScooterProviderList = ({ - intl, classes, openList, scooterProviders, -}) => { +const ScooterProviderList = ({ openList, scooterProviders }) => { + const intl = useIntl(); const renderData = scooterProviders && scooterProviders.length > 0; return ( openList ? ( <> -
    + {intl.formatMessage({ id: 'mobilityPlatform.menu.scooters.list.info' })} -
    + {renderData && scooterProviders.map(item => ( -
    + item.onChangeValue()} /> )} @@ -39,18 +39,32 @@ const ScooterProviderList = ({ )} /> -
    + ))} ) : null ); }; +const StyledContainer = styled.div(({ theme }) => ({ + textAlign: 'left', + padding: theme.spacing(1.5), + borderBottom: '1px solid #6f7276', +})); + +const StyledCheckboxContainer = styled.div(({ theme }) => ({ + borderBottom: '1px solid #6f7276', + display: 'flex', + flexDirection: 'column', + justifyContent: 'start', + paddingLeft: theme.spacing(3.5), +})); + ScooterProviderList.propTypes = { - intl: PropTypes.objectOf(PropTypes.any).isRequired, - classes: PropTypes.objectOf(PropTypes.any).isRequired, openList: PropTypes.bool, - scooterProviders: PropTypes.arrayOf(PropTypes.object), + scooterProviders: PropTypes.arrayOf(PropTypes.shape({ + id: PropTypes.string, + })), }; ScooterProviderList.defaultProps = { diff --git a/src/views/MobilitySettingsView/components/ScooterProviderList/index.js b/src/views/MobilitySettingsView/components/ScooterProviderList/index.js index 8f9dcde79..9b2aa6dc6 100644 --- a/src/views/MobilitySettingsView/components/ScooterProviderList/index.js +++ b/src/views/MobilitySettingsView/components/ScooterProviderList/index.js @@ -1,6 +1,3 @@ -import { withStyles } from '@mui/styles'; -import { injectIntl } from 'react-intl'; import ScooterProviderList from './ScooterProviderList'; -import styles from './styles'; -export default withStyles(styles)(injectIntl(ScooterProviderList)); +export default ScooterProviderList; diff --git a/src/views/MobilitySettingsView/components/ScooterProviderList/styles.js b/src/views/MobilitySettingsView/components/ScooterProviderList/styles.js deleted file mode 100644 index ca288b6ee..000000000 --- a/src/views/MobilitySettingsView/components/ScooterProviderList/styles.js +++ /dev/null @@ -1,18 +0,0 @@ -const styles = theme => ({ - paragraph: { - textAlign: 'left', - padding: theme.spacing(1.5), - }, - border: { - borderBottom: '1px solid #6f7276', - }, - checkBoxContainer: { - borderBottom: '1px solid #6f7276', - display: 'flex', - flexDirection: 'column', - justifyContent: 'start', - paddingLeft: theme.spacing(3.5), - }, -}); - -export default styles; diff --git a/src/views/MobilitySettingsView/components/SpeedLimitZonesList/SpeedLimitZonesList.js b/src/views/MobilitySettingsView/components/SpeedLimitZonesList/SpeedLimitZonesList.js index afdd60bf5..6745a0560 100644 --- a/src/views/MobilitySettingsView/components/SpeedLimitZonesList/SpeedLimitZonesList.js +++ b/src/views/MobilitySettingsView/components/SpeedLimitZonesList/SpeedLimitZonesList.js @@ -1,62 +1,90 @@ import React from 'react'; import { Checkbox, FormControlLabel, Typography } from '@mui/material'; import PropTypes from 'prop-types'; +import { useIntl } from 'react-intl'; +import styled from '@emotion/styled'; const SpeedLimitZonesList = ({ - classes, intl, openSpeedLimitList, speedLimitListAsc, speedLimitSelections, setState, -}) => (openSpeedLimitList ? ( - <> -
    - - {intl.formatMessage({ id: 'mobilityPlatform.menu.speedLimitZones.select' })} - -
    -
    - {openSpeedLimitList && speedLimitListAsc.reduce((acc, curr) => { - acc.push( -
    - setState(curr)} - /> + openSpeedLimitList, speedLimitListAsc, speedLimitSelections, setState, +}) => { + const intl = useIntl(); + + return (openSpeedLimitList ? ( + <> + + + {intl.formatMessage({ id: 'mobilityPlatform.menu.speedLimitZones.select' })} + + + + {openSpeedLimitList && speedLimitListAsc.reduce((acc, curr) => { + acc.push( + + setState(curr)} + /> )} - label={( - - {intl.formatMessage( - { - id: 'mobilityPlatform.content.speedLimitZones.suffix', - }, - { curr }, - )} - + label={( + + {intl.formatMessage( + { + id: 'mobilityPlatform.content.speedLimitZones.suffix', + }, + { curr }, + )} + )} - /> -
    , - ); - return acc; - }, [])} -
    - -) : null); + /> + , + ); + return acc; + }, [])} + + + ) : null); +}; + +const StyledContainer = styled.div(({ theme }) => ({ + textAlign: 'left', + padding: theme.spacing(1.5), + borderBottom: '1px solid #6f7276', +})); + +const StyledCheckboxContainer = styled.div(() => ({ + width: '100%', + borderBottom: '1px solid #6f7276', + display: 'flex', + flexDirection: 'column', + justifyContent: 'start', +})); + +const StyledButtonList = styled.div(() => ({ + display: 'flex', + flexDirection: 'row', + flexWrap: 'wrap', + justifyContent: 'left', +})); + +const StyledCheckBox = styled(Checkbox)(({ theme }) => ({ + marginLeft: theme.spacing(4), +})); SpeedLimitZonesList.propTypes = { - intl: PropTypes.objectOf(PropTypes.any).isRequired, - classes: PropTypes.objectOf(PropTypes.any).isRequired, openSpeedLimitList: PropTypes.bool, speedLimitListAsc: PropTypes.arrayOf(PropTypes.number), speedLimitSelections: PropTypes.arrayOf(PropTypes.number), diff --git a/src/views/MobilitySettingsView/components/SpeedLimitZonesList/__tests__/SpeedLimitZonesList.test.js b/src/views/MobilitySettingsView/components/SpeedLimitZonesList/__tests__/SpeedLimitZonesList.test.js index a34c4ed45..db3eec07e 100644 --- a/src/views/MobilitySettingsView/components/SpeedLimitZonesList/__tests__/SpeedLimitZonesList.test.js +++ b/src/views/MobilitySettingsView/components/SpeedLimitZonesList/__tests__/SpeedLimitZonesList.test.js @@ -28,14 +28,4 @@ describe('', () => { expect(p[2].textContent).toContain(`${mockProps.speedLimitListAsc[1]} km/t`); expect(p[3].textContent).toContain(`${mockProps.speedLimitListAsc[2]} km/t`); }); - - it('does contain aria-label attribute', () => { - const { container } = renderWithProviders(); - - const p = container.querySelectorAll('p'); - expect(p[0].getAttribute('aria-label')).toContain(finnishTranslations['mobilityPlatform.menu.speedLimitZones.select']); - expect(p[1].getAttribute('aria-label')).toContain(`${mockProps.speedLimitListAsc[0]} km/t`); - expect(p[2].getAttribute('aria-label')).toContain(`${mockProps.speedLimitListAsc[1]} km/t`); - expect(p[3].getAttribute('aria-label')).toContain(`${mockProps.speedLimitListAsc[2]} km/t`); - }); }); diff --git a/src/views/MobilitySettingsView/components/SpeedLimitZonesList/__tests__/__snapshots__/SpeedLimitZonesList.test.js.snap b/src/views/MobilitySettingsView/components/SpeedLimitZonesList/__tests__/__snapshots__/SpeedLimitZonesList.test.js.snap index 732a44a35..e3bb7d4e7 100644 --- a/src/views/MobilitySettingsView/components/SpeedLimitZonesList/__tests__/__snapshots__/SpeedLimitZonesList.test.js.snap +++ b/src/views/MobilitySettingsView/components/SpeedLimitZonesList/__tests__/__snapshots__/SpeedLimitZonesList.test.js.snap @@ -3,7 +3,7 @@ exports[` should work 1`] = `

    should work 1`] = `