Skip to content

Commit c52c230

Browse files
authored
Merge pull request #5803 from HSLdevcom/trafficnow-v3
Current traffic now page from next to v3 behind a feature flag
2 parents 8a1e9fd + 4f23139 commit c52c230

52 files changed

Lines changed: 3193 additions & 518 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

app/component/Badge.js

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import React from 'react';
2+
import PropTypes from 'prop-types';
3+
import cx from 'classnames';
4+
import { FormattedMessage } from 'react-intl';
5+
import capitalize from 'lodash/capitalize';
6+
import Icon from './Icon';
7+
import { AlertSeverityLevelType } from '../constants';
8+
9+
const DISRUPTION_BADGE_PREFIX = 'disruption-badge-';
10+
11+
function variantValidator(props, propName, componentName) {
12+
if (!Object.values(AlertSeverityLevelType).includes(props[propName])) {
13+
return new Error(
14+
`Invalid prop \`${propName}: ${props[propName]}\` supplied to ${componentName}.`,
15+
);
16+
}
17+
return null;
18+
}
19+
20+
const getIcon = variant => {
21+
switch (true) {
22+
case [AlertSeverityLevelType.Info, AlertSeverityLevelType.Unknown].includes(
23+
variant,
24+
): {
25+
return <Icon img="icon_info-circled" className="info" />;
26+
}
27+
case variant === AlertSeverityLevelType.Warning: {
28+
return <Icon img="icon_alert-circled" className="warning" />;
29+
}
30+
case variant === AlertSeverityLevelType.Severe: {
31+
return <Icon img="icon_caution_white_exclamation" className="danger" />;
32+
}
33+
default:
34+
return null;
35+
}
36+
};
37+
38+
export default function Badge({
39+
label,
40+
showIcon,
41+
variant,
42+
className,
43+
...rest
44+
}) {
45+
return (
46+
<div
47+
{...rest}
48+
className={cx('badge tag-bold', variant.toLowerCase(), className)}
49+
>
50+
{showIcon && getIcon(variant)}
51+
<FormattedMessage
52+
id={`${DISRUPTION_BADGE_PREFIX}${label.toLowerCase()}`}
53+
defaultMessage={capitalize(label.toLowerCase()).replace(/_/g, ' ')}
54+
/>
55+
</div>
56+
);
57+
}
58+
59+
Badge.propTypes = {
60+
label: PropTypes.string,
61+
showIcon: PropTypes.bool,
62+
variant: variantValidator,
63+
className: PropTypes.string,
64+
};
65+
Badge.defaultProps = {
66+
label: undefined,
67+
variant: 'info',
68+
showIcon: false,
69+
className: undefined,
70+
};

app/component/Card.js

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
import PropTypes from 'prop-types';
2-
import React from 'react';
2+
import React, { forwardRef } from 'react';
33
import cx from 'classnames';
44

5-
export default function Card({ className, children }) {
6-
return <div className={cx('card', className)}>{children}</div>;
7-
}
5+
const Card = forwardRef(({ className, children, ...rest }, ref) => {
6+
return (
7+
<div ref={ref} className={cx('card', className)} {...rest}>
8+
{children}
9+
</div>
10+
);
11+
});
812

913
Card.displayName = 'Card';
1014

@@ -14,3 +18,5 @@ Card.propTypes = {
1418
};
1519

1620
Card.defaultProps = { className: undefined };
21+
22+
export default Card;

app/component/DisruptionBannerAlert.js

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { configShape, alertShape } from '../util/shapes';
77
import Icon from './Icon';
88
import TruncatedMessage from './TruncatedMessage';
99
import { mapAlertSource } from '../util/alertUtils';
10+
import { TRAFFICNOW } from '../util/path';
1011

1112
const DisruptionBannerAlert = (
1213
{ language, alert, openAllAlerts, truncate, onClose },
@@ -76,9 +77,13 @@ const DisruptionBannerAlert = (
7677
<a
7778
className="disruption-info-content"
7879
onClick={e => e.stopPropagation()}
79-
href={`${config.URL.ROOTLINK}/${
80-
language === 'fi' ? '' : `${language}/`
81-
}${config.trafficNowLink[language]}`}
80+
href={
81+
config.trafficNowTest
82+
? `/${TRAFFICNOW}`
83+
: `${config.URL.ROOTLINK}/${
84+
language === 'fi' ? '' : `${language}/`
85+
}${config.trafficNowLink[language]}`
86+
}
8287
>
8388
{message}
8489
</a>

app/component/DisruptionInfoButton.js

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,17 @@ function DisruptionInfoButton(props, { config }) {
1313
className="cursor-pointer disruption-info noborder"
1414
onClick={props.openDisruptionInfo}
1515
>
16-
<FormattedMessage
17-
id="disruptions-and-diversions"
18-
defaultMessage="Disruptions and diversions"
19-
/>
16+
{config.trafficNowTest ? (
17+
<FormattedMessage
18+
id="traffic-now-long"
19+
defaultMessage="Services now"
20+
/>
21+
) : (
22+
<FormattedMessage
23+
id="disruptions-and-diversions"
24+
defaultMessage="Disruptions and diversions"
25+
/>
26+
)}
2027
{props.viewer?.alerts?.length > 0 && (
2128
<Icon
2229
img="icon_caution_white_exclamation"

app/component/DisruptionInfoButtonContainer.js

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,22 @@
11
import PropTypes from 'prop-types';
22
import React, { useContext } from 'react';
33
import { graphql, QueryRenderer } from 'react-relay';
4-
import { matchShape, routerShape } from 'found';
54
import ReactRelayContext from 'react-relay/lib/ReactRelayContext';
65
import DisruptionInfoButton from './DisruptionInfoButton';
76
import { addAnalyticsEvent } from '../util/analyticsUtils';
87

9-
function DisruptionInfoButtonContainer(outerProps, { config: { feedIds } }) {
10-
const { setDisruptionInfoOpen } = outerProps;
8+
function DisruptionInfoButtonContainer(
9+
{ onClick = () => {} },
10+
{ config: { feedIds } },
11+
) {
1112
const { environment } = useContext(ReactRelayContext);
1213
const openDisruptionInfo = () => {
13-
setDisruptionInfoOpen(true);
1414
addAnalyticsEvent({
1515
category: 'Navigation',
1616
action: 'OpenDisruptions',
1717
name: null,
1818
});
19+
onClick();
1920
};
2021

2122
return (
@@ -42,12 +43,10 @@ function DisruptionInfoButtonContainer(outerProps, { config: { feedIds } }) {
4243
}
4344

4445
DisruptionInfoButtonContainer.propTypes = {
45-
setDisruptionInfoOpen: PropTypes.func.isRequired,
46+
onClick: PropTypes.func,
4647
};
4748

4849
DisruptionInfoButtonContainer.contextTypes = {
49-
router: routerShape.isRequired,
50-
match: matchShape.isRequired,
5150
config: PropTypes.shape({
5251
feedIds: PropTypes.arrayOf(PropTypes.string.isRequired),
5352
}).isRequired,

app/component/Gutterer.js

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import React from 'react';
2+
import PropTypes from 'prop-types';
3+
import cx from 'classnames';
4+
import { stylesShape } from '../util/shapes';
5+
6+
const Gutterer = ({
7+
children,
8+
className,
9+
containerStyles,
10+
leftGutterStyles,
11+
rightGutterStyles,
12+
contentStyles,
13+
maxWidth,
14+
}) => {
15+
const contentStylesWithMaxWidth = {
16+
...contentStyles,
17+
maxWidth,
18+
margin: maxWidth ? '0 auto' : undefined,
19+
width: maxWidth ? '100%' : undefined,
20+
};
21+
22+
return (
23+
<div className={cx('gutterer', className)} style={containerStyles}>
24+
<div className="left-gutter" style={leftGutterStyles} />
25+
<div className="gutterer-content" style={contentStylesWithMaxWidth}>
26+
{children}
27+
</div>
28+
<div className="right-gutter" style={rightGutterStyles} />
29+
</div>
30+
);
31+
};
32+
33+
Gutterer.propTypes = {
34+
children: PropTypes.node.isRequired,
35+
className: PropTypes.string,
36+
maxWidth: PropTypes.string.isRequired,
37+
containerStyles: stylesShape,
38+
leftGutterStyles: stylesShape,
39+
rightGutterStyles: stylesShape,
40+
contentStyles: stylesShape,
41+
};
42+
43+
Gutterer.defaultProps = {
44+
className: undefined,
45+
containerStyles: {},
46+
leftGutterStyles: {},
47+
rightGutterStyles: {},
48+
contentStyles: {},
49+
};
50+
51+
export default Gutterer;

app/component/Icon.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,8 @@ const Icon = ({
3535
{background}
3636
<g
3737
style={{
38-
fill: fill || color || null,
38+
color: color || null,
39+
fill: color || null,
3940
height: height ? `${height}em` : null,
4041
width: width ? `${width}em` : null,
4142
outline: 0,

app/component/IndexPage.js

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ import {
4747
} from '../action/PositionActions';
4848
import FavouriteStore from '../store/FavouriteStore';
4949
import { useConfigContext } from '../configurations/ConfigContext';
50+
import TrafficNowLinkNew from './trafficnow/TrafficNowLink';
5051

5152
const StopRouteSearch = withSearchContext(DTAutoSuggest);
5253
const LocationSearch = withSearchContext(DTAutosuggestPanel);
@@ -190,12 +191,6 @@ function IndexPage(props, context) {
190191
executeAction(storeDestination, favourite);
191192
};
192193

193-
const trafficNowHandler = (e, lang) => {
194-
window.location = `${config.URL.ROOTLINK}/${
195-
lang === 'fi' ? '' : `${lang}/`
196-
}${config.trafficNowLink[lang]}`;
197-
};
198-
199194
const clickStopNearIcon = url => {
200195
addAnalyticsEvent({
201196
event: 'sendMatomoEvent',
@@ -261,7 +256,12 @@ function IndexPage(props, context) {
261256
);
262257
};
263258

264-
const { trafficNowLink } = config;
259+
const { trafficNowLink, trafficNowTest } = config;
260+
const trafficNowHref = trafficNowLink
261+
? `${config.URL.ROOTLINK}/${language === 'fi' ? '' : `${language}/`}${
262+
config.trafficNowLink[language]
263+
}`
264+
: undefined;
265265
const { breakpoint } = props;
266266

267267
const origin = pendingOriginRef.current || props.origin;
@@ -391,9 +391,17 @@ function IndexPage(props, context) {
391391
</>
392392
)}
393393

394-
{trafficNowLink && (
395-
<TrafficNowLink lang={language} handleClick={trafficNowHandler} />
394+
{trafficNowLink && !trafficNowTest && (
395+
<TrafficNowLink
396+
handleClick={(e, lang) => {
397+
window.location = `${config.URL.ROOTLINK}/${
398+
lang === 'fi' ? '' : `${lang}/`
399+
}${config.trafficNowLink[lang]}`;
400+
}}
401+
href={trafficNowHref}
402+
/>
396403
)}
404+
{trafficNowTest && <TrafficNowLinkNew />}
397405
</CtrlPanel>
398406
</div>
399407
{(showSpinner && <OverlayWithSpinner />) || null}
@@ -431,13 +439,17 @@ function IndexPage(props, context) {
431439
<StopRouteSearch isMobile {...stopRouteSearchProps} />
432440
</div>
433441
<CtrlPanel.SeparatorLine usePaddingBottom20 />
434-
{trafficNowLink && (
442+
{trafficNowLink && !trafficNowTest && (
435443
<TrafficNowLink
436-
lang={language}
437-
handleClick={trafficNowHandler}
438-
fontWeights={fontWeights}
444+
handleClick={(e, lang) => {
445+
window.location = `${config.URL.ROOTLINK}/${
446+
lang === 'fi' ? '' : `${lang}/`
447+
}${config.trafficNowLink[lang]}`;
448+
}}
449+
href={trafficNowHref}
439450
/>
440451
)}
452+
{trafficNowTest && <TrafficNowLinkNew />}
441453
</CtrlPanel>
442454
</div>
443455
</div>

app/component/MainMenu.js

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import React, { useState } from 'react';
33
import { FormattedMessage, useIntl } from 'react-intl';
44
import Link from 'found/Link';
55
import { connectToStores } from 'fluxible-addons-react';
6+
import { useRouter } from 'found';
67
import { configShape } from '../util/shapes';
78
import DisruptionInfoButtonContainer from './DisruptionInfoButtonContainer';
89
import Icon from './Icon';
@@ -13,9 +14,11 @@ import { updateCountries } from '../action/CountryActions';
1314
import Toggle from './Toggle';
1415
import searchContext from '../util/searchContext';
1516
import intializeSearchContext from '../util/DTSearchContextInitializer';
17+
import { TRAFFICNOW } from '../util/path';
1618

1719
function MainMenu(props, { config, executeAction }) {
1820
const intl = useIntl();
21+
const { router } = useRouter();
1922
const [countries, setCountries] = useState(props.countries);
2023
const appBarLink =
2124
config.appBarLink?.altLink?.[props.currentLanguage] || config.appBarLink;
@@ -61,7 +64,14 @@ function MainMenu(props, { config, executeAction }) {
6164
{config.mainMenu.showDisruptions && (
6265
<div className="offcanvas-section">
6366
<DisruptionInfoButtonContainer
64-
setDisruptionInfoOpen={props.setDisruptionInfoOpen}
67+
onClick={
68+
config.trafficNowTest
69+
? () => {
70+
router.push(`/${TRAFFICNOW}`);
71+
props.closeMenu();
72+
}
73+
: () => props.setDisruptionInfoOpen(true)
74+
}
6575
/>
6676
</div>
6777
)}

app/component/TopLevel.js

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -193,16 +193,18 @@ class TopLevel extends React.Component {
193193
<section id="mainContent" className="content">
194194
{this.props.meta}
195195
<noscript>This page requires JavaScript to run.</noscript>
196-
<ErrorBoundary
197-
key={
198-
this.props.match.location.state &&
199-
this.props.match.location.state.errorBoundaryKey
200-
? this.props.match.location.state.errorBoundaryKey
201-
: 0
202-
}
203-
>
204-
{content}
205-
</ErrorBoundary>
196+
{content && (
197+
<ErrorBoundary
198+
key={
199+
this.props.match.location.state &&
200+
this.props.match.location.state.errorBoundaryKey
201+
? this.props.match.location.state.errorBoundaryKey
202+
: 0
203+
}
204+
>
205+
{content}
206+
</ErrorBoundary>
207+
)}
206208
</section>
207209
</Fragment>
208210
);

0 commit comments

Comments
 (0)